4

I searched a lot before asking but can't seem to find a solution that works with me.

I have a function that I need to be called after the state is set to the new value.

const onClickCallback = () => {
    setState(..., setCallback())
}

const setCallback = () => {
    console.log(State) // this prints the old State value
    if(State === something){
        ....
    }
}

Eventhough the function is being called as the setState callback, it still gets the older value.

I'm not sure if the problem is from my end or it's just not possible..

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Etch
  • 462
  • 3
  • 12
  • 1
    You're immediately invoking the `setCallback` with the `()`. Try just passing the function name `setState(..., setCallback)` – Matt U Sep 29 '21 at 23:48
  • 1
    I tried that but didn't work, I'm not sure either why they closed it but fortunately someone answered it. – Etch Sep 30 '21 at 00:15

2 Answers2

5

Unlike class component's this.setState, function component's useState state updater function doesn't take a second callback argument to be called after any state update.

What you are doing is enqueueing a state update and passing extraneous arguments, one of which is a function that you are immediately invoking, and passing any return value to setState, which will be ignored.

setState(..., setCallback()) // setCallback invoked and result passed

Use an useEffect hook with a dependency on the state to log any state updates.

const onClickCallback = () => {
  setState(...);
};

React.useEffect(() => {
  console.log(State) // this prints the updated state value
  if(state === something){
      ....
  }
}, [state, /* add other dependencies here */]);

useEffect with dependency array acts as an equivalent to componentDidMount and componentDidUpdate. It's the componentDidUpdate lifecycle being used above to "react" to the state update and trigger a side-effect.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • This worked perfectly, thank you so much for the solution and the clear explanation! – Etch Sep 30 '21 at 00:17
  • How to add a callback inside the const function only? useEffect I will have to use outside const, right? – Mehul Parmar Jun 11 '23 at 17:06
  • @MehulParmar Are you asking about adding a separate, independent callback in `onClickCallback`? A callback in the `useEffect` callback? Something else? – Drew Reese Jun 11 '23 at 17:19
  • @DrewReese callback after the state variable is changed. is `useEffect` the only way to do that? For `useEffect` I am getting this error "Invalid hook call. Hooks can only be called inside of the body of a function component." – Mehul Parmar Jun 12 '23 at 04:13
  • @MehulParmar If you need to run some logic after some state updates, yes, use the `useEffect` hook. Regarding the error, React hooks can only be called in React functions or custom React hooks. Where are you calling `useEffect`? – Drew Reese Jun 12 '23 at 04:22
1

First

This matter has nothing to do with React, your approach would have caused the setCallback() function to be executed first.

I edit your code and demo result :

  const onClickCallback = () => {
    // Here will run setCallback function before setState function.
    setState('...', setCallback())
  }

  const setCallback = () => {
    console.info('run setCallback')
  }

  const setState = () => {
    console.info('run setState')
  }

  onClickCallback()

Second

Answer your question, you can use useEffect to achieve your goals.

Simple example:

const {useState, useEffect} = React;

const DemoComponent = (props) => {
  
  const [state, setState] = useState('initialState')

  useEffect(() => {
    if (state === 'something') {
      console.info('state:' + state)
    }
  }, [state])

  const onClickCallback = () => {
    setState('something')
  }

  return (
  <div>
    <button onClick={onClickCallback}>Click</button>
  </div>
  );
}

ReactDOM.render(
  <DemoComponent />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Jay Lu
  • 1,515
  • 8
  • 18