384

React hooks introduces useState for setting component state. But how can I use hooks to replace the callback like below code:

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

I want to do something after the state is updated.

I know I can use useEffect to do the extra things but I have to check the state previous value which requires a bit code. I am looking for a simple solution which can be used with useState hook.

S. Hesam
  • 5,266
  • 3
  • 37
  • 59
Joey Yi Zhao
  • 37,514
  • 71
  • 268
  • 523
  • 2
    in class component, I used async and await to achieve the same result like what you did to add a callback in setState. Unfortunately, it is not working in hook. Even if I added async and await , react will not wait for state to update. Maybe useEffect is the only way to do it. – MING WU May 22 '19 at 00:58
  • There's an easy way to do this without useEffect https://stackoverflow.com/a/70405577/5823517 – Tunn Dec 18 '21 at 17:35

22 Answers22

415

You need to use useEffect hook to achieve this.

const [counter, setCounter] = useState(0);

const doSomething = () => {
  setCounter(123);
}

useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);

If you want the useEffect callback to be ignored during the first initial render, then modify the code accordingly:

import React, { useEffect, useRef } from 'react';

const [counter, setCounter] = useState(0);
const didMount = useRef(false);

const doSomething = () => {
  setCounter(123);
}

useEffect(() => {
  // Return early, if this is the first render:
  if ( !didMount.current ) {
    return didMount.current = true;
  }
  // Paste code to be executed on subsequent renders:
  console.log('Do something after counter has changed', counter);
}, [counter]);
Kostas Minaidis
  • 4,681
  • 3
  • 17
  • 25
Zohaib Ijaz
  • 21,926
  • 7
  • 38
  • 60
  • 111
    This will fire the `console.log` on the first render as well as any time `counter` changes. What if you only want to do something after the state has been updated but not on initial render as the initial value is set? I guess you could check the value in `useEffect` and decide if you want to do something then. Would that be considered best practice? – Darryl Young Jun 06 '19 at 09:20
  • 3
    To avoid running `useEffect` on initial render, you can create a **custom `useEffect` hook**, which doesn't run on initial render. To create such a hook, you can check out this question: https://stackoverflow.com/questions/53253940/make-react-useeffect-hook-not-run-on-initial-render –  Mar 26 '20 at 16:24
  • 73
    And what about case, when i want to call different callbacks in different setState calls, which will change same state value? You answer is wrong, and in shouldn't be marked as correct to not confusing newbies. The true is that setState callbacks its one of the hardest problems while migrating on hooks from classes which hasn't one clear solving method. Sometimes you really wil be enougth some value-depending effect, and sometimes it'll requires some hacky methods, like saving some kind of flags in Refs. – Dmitry Lobov Jun 03 '20 at 08:51
  • Apparently, today, I could call setCounter(value, callback) so that some task is executed after state is updated, avoiding a useEffect. I'm not entirely sure if this is a preference or a particular benefit. – Ken Ingram Sep 15 '20 at 15:40
  • 8
    @KenIngram I just tried that and got this very specific error: `Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().` – Greg Sadetsky Sep 30 '20 at 04:10
  • 24
    `useEffect` cannot be used in all scenarios. sometimes the state gets updated from several places, **but** you wish to use a called only from one of those places. How can you differentiate easily? A callback is perfect in such situations. A hack would be to use another ugly `useState` only to be used so a specific change could be detected. very ugly... – vsync Dec 06 '20 at 22:10
  • 6
    Agree with @vsync , the second callback should be re-introduce back in `useState` hook. It feels like a stepback from `this.setState` from class component. :/ – Kong May 18 '21 at 13:15
  • 2
    This is not a good alternative to what can be achieved with a callback, we only need callback on a particular setState call and not on every state change, and there is no way to identify that using this. – Abhishek Choudhary Oct 18 '21 at 04:22
  • Curious if anyone has come up with a good alternative to the callback for scenarios where you only need one setState call to trigger some side effect. For now, I've been relying on adding a `callbackRef`, storing my callback in it for the associated state, and in `useEffect`, I check if the callbackRef has something, execute it, then set it back to null. It just feels very ugly to be doing it this way. I could wrap it up into a custom hook like others seem to have done similarly, but it still seems very annoying and brittle. Just wondering if there's a better react way. – Azarro May 16 '22 at 10:52
  • It's not the same as callback at setState. When using callback, you specify it in the caller, not when the changes already been applied. The difference is the caller decided if to call the callback, or not, whereas here it's always called. – mimic Sep 19 '22 at 19:29
  • The beauty of react and in particularly hooks. Solve one problem and introduce another. – Organic Jan 23 '23 at 13:28
  • The method to avoid the first render doesn't work because `useEffect` can only return functions. Use an if/else instead of returning – Zach Saucier Mar 06 '23 at 22:14
59

If you want to update previous state then you can do like this in hooks:

const [count, setCount] = useState(0);


setCount(previousCount => previousCount + 1);

Docs:

ptim
  • 14,902
  • 10
  • 83
  • 103
Bimal Grg
  • 7,624
  • 2
  • 24
  • 21
  • 1
    @BimalGrg Does this really work? I cannot replicate this. It fails to compile with this error: `expected as assignment or function call and instead saw an expression` – tonitone120 Nov 30 '20 at 19:30
  • @tonitone120 there is an arrow function inside setCount. – Bimal Grg Dec 03 '20 at 03:09
  • 42
    @BimalGrg The question is asking for the ability to execute code immediately **after** state is updated. Does this code really help with that task? – tonitone120 Dec 03 '20 at 13:20
  • Yes it will work,as like in this.setState in class components, – Aashiq Dec 12 '20 at 05:11
  • 1
    @tonitone120 if you want execute code immediately after state is updated then you have to use useEffect hook. In useEffect you can check whether state updated or not and perform any action accordingly. – Bimal Grg Dec 16 '20 at 03:12
  • Why can't I find this pattern documented anywhere? – Drazen Bjelovuk Feb 17 '22 at 22:38
  • 1
    @DrazenBjelovuk it's documented in the official Hook API doc under the [_Functional Update_ section](https://reactjs.org/docs/hooks-reference.html#functional-updates) of `useState`. – Emile Bergeron Apr 15 '22 at 16:26
  • I don't think this causes code to run *after* state is updated. `previousCount` is the *old* state, not the new, updated state. What's needed is `newState` + a (no longer supported) callback – i-know-nothing Jul 25 '22 at 22:29
53

Mimic setState callback with useEffect, only firing on state updates (not initial state):

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)
useEffect(() => {
  if (isFirstRender.current) {
    isFirstRender.current = false // toggle flag after first render/mounting
    return;
  }
  console.log(state) // do something after state has updated
}, [state])

Custom Hook useEffectUpdate

function useEffectUpdate(callback) {
  const isFirstRender = useRef(true);
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false; // toggle flag after first render/mounting
      return;
    }
    callback(); // performing action after state has updated
  }, [callback]);
}

// client usage, given some state dep
const cb = useCallback(() => { console.log(state) }, [state]); // memoize callback
useEffectUpdate(cb);
ford04
  • 66,267
  • 20
  • 199
  • 171
  • 2
    Alternative: `useState` [can be implemented](https://stackoverflow.com/questions/54954091/how-to-use-callback-with-usestate-hook-in-react/61842546#61842546) to receive a callback like `setState` in classes. – ford04 Sep 11 '20 at 07:55
44

I Think, using useEffect is not an intuitive way.

I created a wrapper for this. In this custom hook, you can transmit your callback to setState parameter instead of useState parameter.

I just created Typescript version. So if you need to use this in Javascript, just remove some type notation from code.

Usage

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

Declaration

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback;

Drawback

If the passed arrow function references a variable outer function, then it will capture current value not a value after the state is updated. In the above usage example, console.log(state) will print 1 not 2.

MJ Studio
  • 3,947
  • 1
  • 26
  • 37
  • 5
    Thanks! cool approach. Created a sample [codesandbox](https://codesandbox.io/s/usestatecallback-forked-kzz2z) – ValYouW Aug 30 '20 at 13:05
  • @ValYouW Thank your for creating sample. The only matter is that if passed arrow function references variable outer function, then it will capture current values not values after state is updated. – MJ Studio Aug 30 '20 at 16:10
  • Yes, that's the catchy part with functional components... – ValYouW Aug 30 '20 at 22:45
  • How to get the previous state value here? – Ankan-Zerob Sep 27 '20 at 12:15
  • 1
    @Ankan-Zerob I think, in the Usage part, the `state` is referring the previous one yet when callback is called. Isn't it? – MJ Studio Sep 29 '20 at 12:54
  • @MJStudio Hi MJ, thank you for your answer, it is mostly correct, but I'm unable to refer to a specific key in your example. Just try `setState({name: 'Bob'}, (n) => {console.log(n.name)})`. To be more specific I'll get an error here `n.name`, how it could be fixed? – Andrew Dec 23 '20 at 12:55
  • @Andrew Is it TypeScript error? In my case, const [s, setS] = useStateCallback({ name: 'mj' }); useEffect(() => { setS({ name: 'Bob' }, (n) => { console.log(n.name); }); }, [setS]); work well as print `Bob` not `mj` but TypeScript invoke a error that n can be `undefined` – MJ Studio Dec 26 '20 at 07:57
  • ```const [state, setState] = useStateCallback(1);setState(2, (n) => {console.log(n, state) });```why in above use: n is 2 but state is still 1, and there is anyway that state can also print 2? – Jochen Shi Dec 21 '21 at 03:27
  • not working to me – famfamfam Jun 29 '22 at 10:59
19

I was running into the same problem, using useEffect in my setup didn't do the trick (I'm updating a parent's state from an array multiple child components and I need to know which component updated the data).

Wrapping setState in a promise allows to trigger an arbitrary action after completion:

import React, {useState} from 'react'

function App() {
  const [count, setCount] = useState(0)

  function handleClick(){
    Promise.resolve()
      .then(() => { setCount(count => count+1)})
      .then(() => console.log(count))
  }


  return (
    <button onClick= {handleClick}> Increase counter </button>
  )
}

export default App;

The following question put me in the right direction: Does React batch state update functions when using hooks?

Hugo Merzisen
  • 303
  • 2
  • 8
15

I wrote custom hook with typescript if anyone still needs it.

import React, { useEffect, useRef, useState } from "react";

export const useStateWithCallback = <T>(initialState: T): [state: T, setState: (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => void] => {
    const [state, setState] = useState<T>(initialState);
    const callbackRef = useRef<(updated: T) => void>();

    const handleSetState = (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => {
        callbackRef.current = callback;
        setState(updatedState);
    };

    useEffect(() => {
        if (typeof callbackRef.current === "function") {
            callbackRef.current(state);
            callbackRef.current = undefined;
        }
    }, [state]);

    return [state, handleSetState];
}
Giorgi_Mdivani
  • 373
  • 1
  • 5
  • 12
  • 3
    This is the correct approach IMHO. The other examples don't take into account that you might have different callbacks in your code depending on where you're calling setState - sometimes you might want one callback, sometimes another, sometimes none. – Robert Rendell Sep 19 '21 at 01:35
10

setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state.

setState method is asynchronous, and as a matter of fact, it does not return a promise. So In cases where we want to update or call a function, the function can be called callback in setState function as the second argument. For example, in your case above, you have called a function as a setState callback.

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

The above code works fine for class component, but in the case of functional component, we cannot use the setState method, and this we can utilize the use effect hook to achieve the same result.

The obvious method, that comes into mind is that ypu can use with useEffect is as below:

const [state, setState] = useState({ name: "Michael" })

useEffect(() => {
  console.log(state) // do something after state has updated
}, [state])

But this would fire on the first render as well, so we can change the code as follows where we can check the first render event and avoid the state render. Therefore the implementation can be done in the following way:

We can use the user hook here to identify the first render.

The useRef Hook allows us to create mutable variables in functional components. It’s useful for accessing DOM nodes/React elements and to store mutable variables without triggering a re-render.

const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);

useEffect(() => {
 if (!firstTimeRender.current) {
    console.log(state);
  }
}, [state])

useEffect(() => { 
  firstTimeRender.current = false 
}, [])
Laura Nutt
  • 303
  • 2
  • 6
  • 1
    this help me a lot, thanks. I would like to add something because it was giving me failures at first, the order of the useEffect functions is very important. note that you have to write first the useEffect with dependencies and later the "componentDidMount" with no dependecies. that is, as it is in the example. Thanks again. – Carlos Martínez Jan 27 '21 at 04:54
10

you can use following ways I knew to get the lastest state after updating:

  1. useEffect
    https://reactjs.org/docs/hooks-reference.html#useeffect
    const [state, setState] = useState({name: "Michael"});
    
    const handleChangeName = () => {
      setState({name: "Jack"});
    }
    
    useEffect(() => {
      console.log(state.name); //"Jack"

      //do something here
    }, [state]);
  1. functional update
    https://reactjs.org/docs/hooks-reference.html#functional-updates
    "If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value. "
    const [state, setState] = useState({name: "Michael"});

    const handleChangeName = () => {
      setState({name: "Jack"})
      setState(prevState => {
        console.log(prevState.name);//"Jack"

        //do something here

        // return updated state
        return prevState;
      });
    }
  1. useRef
    https://reactjs.org/docs/hooks-reference.html#useref
    "The returned ref object will persist for the full lifetime of the component."
    const [state, setState] = useState({name: "Michael"});

    const stateRef = useRef(state);
    stateRef.current  = state;
    const handleClick = () => {
      setState({name: "Jack"});

      setTimeout(() => {
        //it refers to old state object
        console.log(state.name);// "Michael";

        //out of syntheticEvent and after batch update
        console.log(stateRef.current.name);//"Jack"

        //do something here
      }, 0);
    }

In react syntheticEvent handler, setState is a batch update process, so every change of state will be waited and return a new state.
"setState() does not always immediately update the component. It may batch or defer the update until later. ",
https://reactjs.org/docs/react-component.html#setstate

Here is a useful link
Does React keep the order for state updates?

Kaiwen Luo
  • 370
  • 4
  • 10
2

I had a use case where I wanted to make an api call with some params after the state is set. I didn't want to set those params as my state so I made a custom hook and here is my solution

import { useState, useCallback, useRef, useEffect } from 'react';
import _isFunction from 'lodash/isFunction';
import _noop from 'lodash/noop';

export const useStateWithCallback = initialState => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(_noop);

  const handleStateChange = useCallback((updatedState, callback) => {
    setState(updatedState);
    if (_isFunction(callback)) callbackRef.current = callback;
  }, []);

  useEffect(() => {
    callbackRef.current();
    callbackRef.current = _noop; // to clear the callback after it is executed
  }, [state]);

  return [state, handleStateChange];
};
Akash Singh
  • 547
  • 5
  • 8
1

Your question is very valid.Let me tell you that useEffect run once by default and after every time the dependency array changes.

check the example below::

import React,{ useEffect, useState } from "react";

const App = () => {
  const [age, setAge] = useState(0);
  const [ageFlag, setAgeFlag] = useState(false);

  const updateAge = ()=>{
    setAgeFlag(false);
    setAge(age+1);
    setAgeFlag(true);
  };

  useEffect(() => {
    if(!ageFlag){
      console.log('effect called without change - by default');
    }
    else{
      console.log('effect called with change ');
    }
  }, [ageFlag,age]);

  return (
    <form>
      <h2>hooks demo effect.....</h2>
      {age}
      <button onClick={updateAge}>Text</button>
    </form>
  );
}

export default App;

If you want the setState callback to be executed with the hooks then use flag variable and give IF ELSE OR IF block inside useEffect so that when that conditions are satisfied then only that code block execute. Howsoever times effect runs as dependency array changes but that IF code inside effect will execute only on that specific conditions.

arjun sah
  • 407
  • 3
  • 11
  • 4
    This won't work. You don't know in which order the three statements inside updateAge will actually work. All three are async. The Only thing guaranteed is that first line runs before 3rd (since they work on same state). You don't know anything about 2nd line. This example is too simle to see this. – Mohit Singh Mar 30 '20 at 07:51
  • My friend mohit. I have implemented this technique in a big complex react project when i was moving from react classes to hooks and it works perfectly. Simply try the same logic anywhere in hooks for replacing setState callback and you will know. – arjun sah Apr 19 '20 at 20:25
  • 4
    "works in my project isn't an explanation" , read the docs. They are not synchronous at all. You can't say for sure that the three lines in updateAge would work in that order. If it was in sync then whats need of flag, directly call console.log() line after setAge. – Mohit Singh Apr 20 '20 at 04:56
  • 1
    useRef is a much better solution for "ageFlag". – Dr.Flink May 26 '20 at 06:58
1

We can write a hook called useScheduleNextRenderCallback that returns a "schedule" function. After we call setState, we can call the "schedule" function, passing a callback that we want to run on the next render.

import { useCallback, useEffect, useRef } from "react";

type ScheduledCallback = () => void;
export const useScheduleNextRenderCallback = () => {
  const ref = useRef<ScheduledCallback>();

  useEffect(() => {
    if (ref.current !== undefined) {
      ref.current();
      ref.current = undefined;
    }
  });

  const schedule = useCallback((fn: ScheduledCallback) => {
    ref.current = fn;
  }, []);

  return schedule;
};

Example usage:

const App = () => {
  const scheduleNextRenderCallback = useScheduleNextRenderCallback();

  const [state, setState] = useState(0);

  const onClick = useCallback(() => {
    setState(state => state + 1);
    scheduleNextRenderCallback(() => {
      console.log("next render");
    });
  }, []);

  return <button onClick={onClick}>click me to update state</button>;
};

Reduced test case: https://stackblitz.com/edit/react-ts-rjd9jk

Oliver Joseph Ash
  • 3,138
  • 2
  • 27
  • 47
1

Simple solution, Just install

npm i use-state-with-callback

import React from 'react';
import { useStateWithCallbackLazy } from "use-state-with-callback";

const initialFilters = {
  smart_filter: "",
};

const MyCallBackComp = () => {
  const [filters, setFilters] = useStateWithCallbackLazy(initialFilters);

  const filterSearchHandle = (e) => {
    setFilters(
      {
        ...filters,
        smart_filter: e,
      },
      (value) => console.log("smartFilters:>", value)
    );
  };

  return (
    <Input
      type="text"
      onChange={(e) => filterSearchHandle(e.target.value)}
      name="filter"
      placeholder="Search any thing..."
    />
  );
};

credited to: REACT USESTATE CALLBACK

Hamayun
  • 19
  • 2
1

Edited

Using promise here seems still postpone the execution after rerender, triggering setState twice may be the best solution to get the latest state. Because the setState will be listed and we just need to get prevState to use before rerendering.

Original Post

I just figured out if we can use a Promise here to let setState become awaitable.

Here is my experiment result, feels better then using a callback

Mainly temp the resolve function to trigger in useEffect

function useAsyncState(initialState) {
  const [state, setState] = useState(initialState)
  const resolveCb = useRef()

  const handleSetState = (updatedState) => new Promise((resolve, reject) => {
    // force previous promise resolved
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(updatedState)
    }
    resolveCb.current = resolve
    try {
      setState(updatedState)
    } catch(err) {
      resolveCb.current = undefined
      reject(err)
    }
  })

  useEffect(() => {
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(state)
      resolveCb.current = undefined
    }
  }, [state])

  return [state, handleSetState]
}

using in component

function App() {
  const [count, setCount] = useAsyncState(0)

  const increment = useMemoizedFn(async () => {
    const newCount = await setCount(count + 1)
    console.log(newCount)
  })

  console.log('rerender')

  return (
    <div>
      <h3 onClick={increment}>Hi, {count}</h3>
    </div>
  )
}
Johnny
  • 36
  • 4
1

I have a very specific use case where I needed to render a class in the dom, then set another class. This was my solution which I found to be quite elegant.

const [value1, setValue1] = useState({value: 'whatever', onValue: false})


useEffect(() => {
    setValue1(prev => ({
      value: 'whatever',
      onValue: !prev.onValue, 
    }));
}, ['whatever'])

 
useEffect(() => {

// if you want to ensure the render happens before doThing2() then put it in a timeout of 1ms,
  setTimeout(doThing2, 1); 

// or if you are happy to call it immediately after setting value don't include the timeout
 doThing2()


}, [value1.onValue])
Steve Tomlin
  • 3,391
  • 3
  • 31
  • 63
0

I don't think that distinguish mounted or not with useRef is a good way, isn't a better way by determining the value genetated useState() in useEffect() whether it is the initial value?

const [val, setVal] = useState(null)

useEffect(() => {
  if (val === null) return
  console.log('not mounted, val updated', val)
}, [val])
SiroSong
  • 1
  • 2
0

Until we have native built in support for setState callback, we can do the plain javascript way ... call the function and pass the new variables to it directly.

  const [counter, setCounter] = useState(0);

  const doSomething = () => {
    const newCounter = 123
    setCounter(newCounter);
    doSomethingWCounter(newCounter);
  };

  function doSomethingWCounter(newCounter) {
    console.log(newCounter); // 123
  }
TylerC
  • 95
  • 1
  • 6
0

If you don't need to update state asynchronously you can use a ref to save the value instead of useState.

const name = useRef("John");
name.current = "Michael";
console.log(name.current); // will print "Michael" since updating the ref is not async
0

I explored the use-state-with-callback npm library, and other similar custom hooks, but in the end I realized I can just do something like this:

const [user, setUser] = React.useState(
  {firstName: 'joe', lastName: 'schmo'}
)

const handleFirstNameChange=(val)=> {
  const updatedUser = {
     ...user,
     firstName: val
  }
  setUser(updatedUser)
  updateDatabase(updatedUser)
}
Jar
  • 1,766
  • 1
  • 21
  • 27
  • 2
    In this case that surely works, but if you want to trigger something right after changing the state, but only after actually asynchronously changed then you need `useEffect` or one of the custom hooks which use `useEffect` under the hood. Wanted mention this, as it might be confusing to others – Can Rau Apr 18 '22 at 16:57
-1

What about passing a function?

const [name, setName] = useState(initialName); 
...
setName(() => {
    const nextName = "Michael";
    console.log(nextName);
    return nextName;
  });
Ricola
  • 2,621
  • 12
  • 22
-2

I think what you need is useState and useCallback:

Example Code

import React, { useCallback, useState } from 'react';

const Test = () => {
  const [name, setName] = useState("");
  const testCallback = useCallback(() => console.log(name), [name]);

  return (
    <button onClick={() => {
      setName("Michael")
      testCallback();
    }}>Name</button>
  )
};

export default Test;
-3

How about this:

const [Name, setName] = useState("");
...
onClick={()=>{
setName("Michael")
setName(prevName=>{...}) //prevName is Michael?
}}

james h
  • 115
  • 1
  • 4
  • `prevName` won't be "Michael" as `useState` is async just like `setState` in class components. You can't update the state on one line and assume it's already changed on the next one. You'll likely use the unchanged state. – Zsolt Meszaros Jan 14 '21 at 09:31
  • OK, thanks I just test this code, you are correct, – james h Jan 14 '21 at 09:35
  • It's weird, prevName is Michael, but if u call another function in the callback that use the Name, it's still not updated. – james h Jan 14 '21 at 09:45
-9

UseEffect is the primary solution. But as Darryl mentioned, using useEffect and passing in state as the second parameter has one flaw, the component will run on the initialization process. If you just want the callback function to run using the updated state's value, you could set a local constant and use that in both the setState and the callback.

const [counter, setCounter] = useState(0);

const doSomething = () => {
  const updatedNumber = 123;
  setCounter(updatedNumber);

  // now you can "do something" with updatedNumber and don't have to worry about the async nature of setState!
  console.log(updatedNumber);
}
Kyle Laster
  • 331
  • 4
  • 13