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

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

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

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


当前回答

我遇到了同样的问题,在我的设置中使用useEffect没有做到这一点(我正在从一个数组多个子组件更新父组件的状态,我需要知道哪个组件更新了数据)。

在promise中包装setState允许在完成后触发任意动作:

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;

下面的问题让我找到了正确的方向: React在使用钩子时是否有批量状态更新功能?

其他回答

您需要使用useEffect钩子来实现这一点。

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

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

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

如果你想在第一次初始渲染时忽略useEffect回调,那么相应地修改代码:

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

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

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

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

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

如果你想要更新之前的状态,那么你可以在hooks中这样做:

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


setCount(previousCount => previousCount + 1);

编辑

在这里使用promise似乎仍然推迟了重新渲染后的执行,两次触发setState可能是获得最新状态的最佳解决方案。因为setState将被列出,我们只需要在重新渲染之前获得prevState。

最初的发布

我刚刚弄清楚了我们是否可以在这里使用一个Promise让setState变成可等待的。

这是我的实验结果,感觉比使用回调更好

主要是在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]
}

组件中使用

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

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

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是否保持状态更新的顺序?