I'm still getting my head around react hooks but struggling to see what I'm doing wrong here. I have a component for resizing panels, onmousedown
of an edge I update a value on state then have an event handler for mousemove
which uses this value however it doesn't seem to be updating after the value has changed.
Here is my code:
export default memo(() => { const [activePoint, setActivePoint] = useState(null); // initial is null const handleResize = () => { console.log(activePoint); // is null but should be 'top|bottom|left|right' }; const resizerMouseDown = (e, point) => { setActivePoint(point); // setting state as 'top|bottom|left|right' window.addEventListener('mousemove', handleResize); window.addEventListener('mouseup', cleanup); // removed for clarity }; return ( <div className="interfaceResizeHandler"> {resizePoints.map(point => ( <div key={ point } className={ `interfaceResizeHandler__resizer interfaceResizeHandler__resizer--${ point }` } onMouseDown={ e => resizerMouseDown(e, point) } /> ))} </div> ); });
The problem is with the handleResize
function, this should be using the latest version of activePoint
which would be a string top|left|bottom|right
but instead is null
.
To set a conditional initial value for useState in React:Pass a function to the useState hook. Use a condition to determine the correct initial value for the state variable. The function will only be invoked on the initial render.
The key difference is that the initial value of the state defined by useState can be anything you want it to be. It no longer has to be an object. A string, a number, an object, undefined , null - anything goes!
If you want to set initial state after loading data (fetch data from api) you can use "useEffect" in React hooks. it is equal to "componentWillReceiveProps" in class component.
With React 16.8, function components can now use state. Before this, data from the state had to be passed down as props from class components to function components or you had to convert your function component to a class component. Now, we can use React hooks, and to use state we can use the useState hook.
We call the useState Hook directly inside our component: const [count, setCount] = useState (0); declares a state variable. The only argument to the useState () Hook is the initial state. useState returns a pair of values: the current state and a function that updates it.
Only Call Hooks from React Functions: you must call useState () only inside the functional component or a custom hook. Let's follow examples of correct and incorrect usage of useState (). useState () is correctly called at the top level of functional component:
“Create” wouldn’t be quite accurate because the state is only created the first time our component renders. During the next renders, useState gives us the current state. Otherwise it wouldn’t be “state” at all! There’s also a reason why Hook names always start with use. We’ll learn why later in the Rules of Hooks.
Call useState () hook to enable state in a functional component. The first argument of the useState (initialValue) is the state's initial value. [state, setState] = useState (initialValue) returns an array of 2 items: the state value and a state updater function.
useRef
to read future valueCurrently, your issue is that you're reading a value from the past. When you define handleResize
it belongs to that render, therefore, when you rerender, nothing happens to the event listener so it still reads the old value from its render.
To fix this, you should use a ref via useRef
that you keep updated so that you can read the current value.
Example (link to jsfiddle):
const [activePoint, _setActivePoint] = React.useState(null); // define a ref const activePointRef = React.useRef(activePoint); // in place of original `setActivePoint` const setActivePoint = x => { activePointRef.current = x; // keep updated _setActivePoint(x); }; const handleResize = () => { // now when reading `activePointRef.current` you'll // have access to the current state console.log(activePointRef.current); }; const resizerMouseDown = /* still the same */; return /* return is still the same */
You have access to current state from setter function, so you could make it:
const handleResize = () => { setActivePoint(activePoint => { console.log(activePoint); return activePoint; }) };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With