React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:
setState(
{ name: "Michael" },
() => console.log(this.state)
);
我想在状态更新后做一些事情。
我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。
React钩子引入了useState来设置组件状态。但是我如何使用钩子来替换下面的回调代码:
setState(
{ name: "Michael" },
() => console.log(this.state)
);
我想在状态更新后做一些事情。
我知道我可以使用useEffect来做额外的事情,但我必须检查之前的状态值,这需要位代码。我正在寻找一个简单的解决方案,可以使用useState挂钩。
当前回答
setState()将更改排队到组件状态,并告诉React该组件及其子组件需要使用更新后的状态重新呈现。
setState方法是异步的,实际上,它并不返回承诺。在我们想要更新或调用一个函数的情况下,函数可以在setState函数中调用callback作为第二个参数。 例如,在上面的例子中,您调用了一个函数作为setState回调函数。
setState(
{ name: "Michael" },
() => console.log(this.state)
);
上面的代码适用于类组件,但对于函数组件,我们不能使用setState方法,因此我们可以利用use effect钩子来实现相同的结果。
显而易见的方法是,你可以使用useEffect,如下所示:
const [state, setState] = useState({ name: "Michael" })
useEffect(() => {
console.log(state) // do something after state has updated
}, [state])
但这也会在第一次呈现时触发,因此我们可以更改如下代码,检查第一次呈现事件并避免状态呈现。因此,可以通过以下方式实现:
我们可以在这里使用user钩子来标识第一次渲染。
useRef钩子允许我们在函数组件中创建可变变量。它对于访问DOM节点/React元素和存储可变变量而不触发重新渲染非常有用。
const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);
useEffect(() => {
if (!firstTimeRender.current) {
console.log(state);
}
}, [state])
useEffect(() => {
firstTimeRender.current = false
}, [])
其他回答
我有一个非常具体的用例,我需要在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])
我遇到了同样的问题,在我的设置中使用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?
}}
我写了自定义挂钩与typescript,如果有人还需要它。
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];
}
你的问题很有道理。让我告诉您,useEffect在默认情况下运行一次,并在每次依赖数组更改之后运行一次。
请看下面的例子:
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;
如果你想要setState回调与钩子一起执行,那么使用标志变量并在useEffect中给出If ELSE OR If块,以便当条件满足时,只执行该代码块。无论如何,当依赖项数组改变时,效果会运行,但效果中的IF代码只会在特定的条件下执行。