React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:

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

我想在状态更新后做一些事情。

我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。


当前回答

UseEffect是主要的解决方案。但正如Darryl提到的,使用useEffect并将state作为第二个参数传入有一个缺陷,组件将在初始化过程中运行。如果你只想让回调函数使用更新后的状态值运行,你可以设置一个本地常量,并在setState和回调中使用它。

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);
}

其他回答

我有一个用例,我想做一个api调用与一些参数后的状态设置。我不想设置这些参数作为我的状态,所以我做了一个自定义钩子,这是我的解决方案

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];
};

你可以使用以下方法,我知道获得更新后的最新状态:

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]);

功能更新 https://reactjs.org/docs/hooks-reference.html#functional-updates 如果新的状态是用之前的状态计算出来的,你可以传递一个函数给setState。该函数将接收之前的值,并返回更新后的值。”

    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;
      });
    }

useRef https://reactjs.org/docs/hooks-reference.html#useref 返回的ref对象将在组件的整个生命周期内持续存在。

    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);
    }

在react synticevent处理程序中,setState是一个批量更新过程,因此每次状态更改都会等待并返回一个新状态。 setState()并不总是立即更新组件。它可以批处理或延迟更新。", https://reactjs.org/docs/react-component.html#setstate

这里有一个有用的链接 React是否保持状态更新的顺序?

在我们有内置的setState回调支持之前,我们可以用简单的javascript方式…调用该函数并直接将新变量传递给它。

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

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

  function doSomethingWCounter(newCounter) {
    console.log(newCounter); // 123
  }

传递一个函数怎么样?

const [name, setName] = useState(initialName); 
...
setName(() => {
    const nextName = "Michael";
    console.log(nextName);
    return nextName;
  });

我有一个非常具体的用例,我需要在dom中呈现一个类,然后设置另一个类。这就是我的解决方案,我发现它相当优雅。

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])