useComponentWillReceiveUpdate
Change states based on changed props during a re-render. The name of the hook comes from UNSAFE_componentWillReceiveProps
method of class components (opens in a new tab).
Usage
import { useComponentWillReceiveUpdate } from 'foxact/use-component-will-receive-update';
function Component(props) {
const [a, setA] = useState('')
const [b, setB] = useState('')
// when props.x changed, only reset a, not b
useComponentWillReceiveUpdate(() => {
setA('')
}, [props.x]);
}
If you're using useEffect like this:
const [state, setState] = useState(false)
useEffect(() => setState(false), [props.someProp])
Don't do it. See Adjusting some state when a prop changes (opens in a new tab) or Storing information from previous renders (opens in a new tab) It should be like this:
const [prev, setPrev] = useState(state)
if (prev !== state) {
setPrev(state)
setState(false)
}
This hook is a helper for the above pattern.
useComponentWillReceiveUpdate(() => setState(false), [state])
Since React will re-execute the component function immediately in response to the state change, you can early return your component, so React can re-execute the component function earlier.
const changed = useComponentWillReceiveUpdate(() => setState(false), [state]);
// some other hooks
const handleChange = useCallback(() => {
// do something
}, []);
// early return to skip the JSX creation
if (changed) {
return null;
}
return <button onClick={handleChange}>Click me</button>;
This should only apply to states of the current component. Modifying states of other components causes React reporting errors. You may also want to read (Avoid) Notifying parent components about state changes (opens in a new tab) and (Avoid) Passing data to the parent (opens in a new tab).
If you really need to directly modify other components' states (E.g. when working with third-party libraries/components/SDKs where you don't have control of that code), use flushSync
to separate two state updates:
import { flushSync } from 'react-dom'
useComponentWillReceiveUpdate(() => {
// Force React to immediately flush scheduled/batched updates
flushSync(() => setLocalState(false))
// By the time flushSync finishes and reaches this line, the DOM update
// for local state change has finished, you can now safely trigger parent
// components' re-render.
props.setParentState(false)
}, [state])