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

React scheduler executes something despite props not being updated

Asked by Viktor Eriksson
4 years ago.

My component looks something like this:

const Scheduler: FunctionComponent = () => {
	const [counter, setCounter] = useState(2);
	const [daypilotConfig, setDaypilotConfig] = useState({...})
        ...

        const resources: DayPilot.ResourceData[] = useMemo(() => {
		...
		return resources;
	}, [someValue])

        return <>
            <button onClick={()=>setCounter(counter+1)}>Increment</button>{counter}
            <DayPilotScheduler {...daypilotConfig} resources={resources} />
        </>
}

Even if I only click the increment-button and set the counter-state, daypilot does stuff. Among things it does is trigger a scroll-event that fetches the same events again. And there is a delay between pressing my button until the value updates and I guess this is due to the scheduler re-calculates some stuff.

My original problem is when pressing an event in the scheduler and wanting to pop up a modal, I have to set the state setShowModal(true) but there is too much delay before the modal is showing.

Is there anything I can do to avoid this or is there something in the react-scheduler-component ?

Answer posted by Dan Letecky [DayPilot]
4 years ago.

Unless something significant has changed in React with the introduction of state hooks, it works like this:

React uses a single immutable state object to store the state items. Whenever you change any item of the state a new state object will be created and the component will be forced to update. In this case, that means a full Scheduler update. It's usually fast but it's noticeable (100 - 500ms).

Using the state hook (useState) makes it look like the individual state items (counter, daypilotConfig) are independent but they are part of the same state object. There is no way for the Scheduler to detect the actual change - or at least it would deny the React state logic which is designed to avoid deep comparison of state items.

1. I would recommend wrapping <DayPilotScheduler > in a special component that uses its state to only store the Scheduler properties. For communication with other components, you can use props and/or methods. When using props, be careful with mapping the parent component state to props directly as it would replicate the same problem on level up.

2. Inside the wrapper component, you can also avoid using the state to store the Scheduler properties at all. The Scheduler properties can be set using inline attributes of <DayPilotScheduler> element and changes can be made explicitly using the direct api:

this.scheduler.update({cellDuration: 50});

This will also ensure that the optimized update will be used when available (see https://api.daypilot.org/daypilot-scheduler-update/) and generally give you more control over the updates.

Comment posted by Viktor Eriksson
4 years ago.

I'm not an expert on react but the objects I pass to the scheduler should not have been changed. Yes, the state object might have changed but the individual objects I pass to the scheduler should not have changed thus the scheduler should not bother executing anything.

A simpler example where there is no connection at all to the state object:

import React, { FunctionComponent, useState} from 'react'
import { DayPilotScheduler } from "daypilot-pro-react";

const TEST: FunctionComponent = () => {
	const [counter, setCounter] = useState(0);
	return <>
		<button onClick={() => setCounter(counter + 1)}>Increment</button>{counter}
		<DayPilotScheduler
			startDate={"2019-11-08T12:00:00"}
		/>
	</>
}

export default TEST;

Despite only having static props it still executes code when I press the increment button. I can see in the performance monitor of the chrome browser that it is doing something and it enters the componentDidUpdate-method within daypilot.react-min.js.

Shouldnt there be a lifecycle-method called shouldComponentUpdate where you decide if props have been updated and only then a re-render should be made ?

Comment posted by Dan Letecky [DayPilot]
4 years ago.

Good point. I did a few tests and yes, you're right - the static attribute values don't help here. React requests an update whenever any state item is changed, no matter if state items are used in component props.

It is possible to detect changes using shouldComponentUpdate. However, the nextProps object it receives is always a new object. The only way to check for actual changes is to do a deep comparison. There are some 50 Scheduler properties that can be specified, some with deep structure (like resources, events, links). The Angular version of DayPilot does such deep comparison in ngDoCheck (an equivalent of shouldComponentUpdate) and it works for small config objects. However, it hurts performance when displaying real data as the comparison takes time comparable with the update.

In other words, it may help in some scenarios but it's not a clear win.

This mode could be added as an option if you find it helpful.

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