Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React useState hook event handler using initial state

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.

like image 699
Simon Staton Avatar asked Mar 20 '19 16:03

Simon Staton


People also ask

How do I change my initial state of useState?

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.

Does useState need an initial value?

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!

How do you initialize a state in React hooks?

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.

Can you initialize state from a function in React hooks?

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.

How do you use the usestate hook in react?

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.

When to call usestate () inside a react function?

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:

Why do hooks always start with “usestate”?

“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.

How to enable state in a functional component using usestate hook?

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.


2 Answers

useRef to read future value

Currently, 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 */ 
like image 81
Andria Avatar answered Sep 17 '22 00:09

Andria


You have access to current state from setter function, so you could make it:

const handleResize = () => {   setActivePoint(activePoint => {     console.log(activePoint);     return activePoint;   }) }; 
like image 20
machnicki Avatar answered Sep 18 '22 00:09

machnicki