search envelope-o feed check
Home Unanswered Active Tags New Question
user comment-o

I can't access a constant updated by an external useState hook within the daypilot scheduler state

Asked by Michael Wolf
7 months ago.

I have a constant called leave. The value is null until it is set by a dropdown list.

The value gets set and updated okay, I can access it with useEffect hook.

But when I try accessing it in timerangeselect so I can bundle it with args and send it to back end, it’s null or undefined. What could I be doing wrong?

const Scheduler = () => {
  const [leave, setLeave] = useState(null);
  const [config, setConfig] = useState({
    startDate: _start,
    days: 31,
    scale: "Day",
    timeHeaders: [
      { groupBy: "Month" },
      { groupBy: "Day", format: "d" }
    ],
    cellWidthSpec: "Auto",
    cellWidth: 50,
    durationBarVisible: false,
    treeEnabled: true,
    rowHeaderColumns: [
      { name: "Employee" }
    ],
    onEventMoved: args => {
      console.log("Event moved: ", args.e.data.id, args.newStart, args.newEnd, args.newResource);
      getScheduler().message("Event moved: " + args.e.data.text);
    },
    onEventResized: args => {
      console.log("Event resized: ", args.e.data.id, args.newStart, args.newEnd);
      getScheduler().message("Event resized: " + args.e.data.text);
    },
    onTimeRangeSelected: args => {
      DayPilot.Modal.form(form).then(modal => {
        getScheduler().clearSelection();
        console.log("Leave---", leave);

        const _args = {
          id: DayPilot.guid(),
          DayType: _type.id,
          start: args.start,
          end: args.end,
          resource: args.resource,
          leavetype: leave,
        };

        getScheduler().events.add({
          id: DayPilot.guid(),
          text: _type.text,
          start: args.start,
          end: args.end,
          resource: args.resource,
          barColor: "green",
          onRowSelect: redisSync(_args),
        });
      });
    }
  });

  const schedulerRef = useRef();

  const getScheduler = () => schedulerRef.current.control;

  const handleMonthSelect = (value) => {
    setMonth(value);
    console.log(value);
  };

  const redisSync = async (args) => {
    try {
      const response = await axios.post('/leaverequests', args);
      console.log("Post successful");
      console.log('Response:', response.data);
      // fetchRecordsByDesignation(targetDesignation);
    } catch (error) {
      console.error('Error sending data:', error);
    }
  };

  const loadData = (args) => {
    const resources = _resources;
    const events = _events;
    getScheduler().update({
      resources,
      events
    });
  };

  useEffect(() => {
    if (!leave) return;
    console.log("leaves", leave);
  }, [leave]);

  useEffect(() => {
    loadData();
  }, []);

  return (
    <div>
      <div className="toolbar">
        <Row>
          <Col style={{ padding: 15 }}>
            <ProFormSelect
              name="SelectLeavetype"
              label="Select Leavtype"
              style={{ width: 200 }}
              valueEnum={{
                1: "Annual Leave",
                2: "Sick Leave"
              }}
              placeholder="Leave Type"
              onChange={(value) => {
                setLeave(value);
                console.log("value", value);
              }}
            />
          </Col>
        </Row>
      </div>
      <DayPilotScheduler {...config} ref={schedulerRef} />
    </div>
  );
};

export default Scheduler;
Answer posted by Dan Letecky [DayPilot]
7 months ago.

It appears that you're running into a closure issue. When you define a function inside a component, it captures the values of the state and props at the time of the function's creation.

In your case, the onTimeRangeSelected function captures the initial value of leave (which is null) when it is first created, and doesn't re-capture the value when leave changes. This is because the onTimeRangeSelected function doesn't directly depend on the leave state, so it doesn't get recreated when leave changes.

One way to fix this is by using a reference for the leave state. Here's how you can do it:

1. Create a reference for leave:

const leaveRef = useRef();

2. Update this reference whenever leave changes:

useEffect(() => {
    leaveRef.current = leave;
}, [leave]);

3. Access the value from the ref instead of directly from the state within onTimeRangeSelected:

onTimeRangeSelected: args => {
    DayPilot.Modal.form(form).then(modal => {
        getScheduler().clearSelection();

        console.log("Leave---", leaveRef.current);  // Use leaveRef.current here

        const _args = {
            //... other properties
            leavetype: leaveRef.current,  // Use leaveRef.current here
        };

        //... rest of the function
    });
},

This should allow you to access the updated value of leave inside onTimeRangeSelected.

Comment posted by Michael Wolf
7 months ago.

This looks amazing, ill try it out tomorrow and give out the feedback

Comment posted by Michael Wolf
7 months ago.

Great Scott! it worked, id wasted the entire day with a frustrated null field at my back end. I never thought of the useRef hook. Thank alot for this.

—————before

OPTIONS /leaverequests 204 1.306 ms - 0

{

id: '6efd3d41-2363-af54-3808-0f7220157af9',

DayType: 2,

start: '2023-08-14T00:00:00',

end: '2023-08-19T00:00:00',

resource: 'G1258',

leavetype: null

}

after———————

OPTIONS /leaverequests 204 6.621 ms - 0

{

id: 'be22f75d-e86c-5f60-90e4-476f9fd94585',

DayType: 2,

start: '2023-08-07T00:00:00',

end: '2023-08-12T00:00:00',

resource: 'G1258',

leavetype: '4'

}

One observation is the network response time, its now 6 times slower. But that shouldn’t affect any operations.

This question is more than 1 months old and has been closed. Please create a new question if you have anything to add.