React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:
setState(
{ name: "Michael" },
() => console.log(this.state)
);
我想在状态更新后做一些事情。
我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。
React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:
setState(
{ name: "Michael" },
() => console.log(this.state)
);
我想在状态更新后做一些事情。
我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。
当前回答
传递一个函数怎么样?
const [name, setName] = useState(initialName);
...
setName(() => {
const nextName = "Michael";
console.log(nextName);
return nextName;
});
其他回答
我遇到了同样的问题,在我的设置中使用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在使用钩子时是否有批量状态更新功能?
这个怎么样:
const [Name, setName] = useState("");
...
onClick={()=>{
setName("Michael")
setName(prevName=>{...}) //prevName is Michael?
}}
编辑
在这里使用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是否保持状态更新的顺序?
用useEffect模拟setState回调,只在状态更新时触发(不是初始状态):
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])
自定义钩子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);