useState
不会立即更新状态。
我正在使用react-select,我需要使用根据请求结果选择的(
multi
)选项来加载组件。
因此,我创建了状态
defaultOptions
,来存储 queues
常量的值。
事实证明,加载组件时,只有第二次才显示值。
我在
console.log
中做了一个queues
,返回和空不同
我对
defaultOptions
状态做了同样的事情,返回为空。
我创建了一个codesandbox以便更好地查看。
const options = [
{
label: "Queue 1",
value: 1
},
{
label: "Queue 2",
value: 2
},
{
label: "Queue 3",
value: 3
},
{
label: "Queue 4",
value: 4
},
{
label: "Queue 5",
value: 5
}
];
const CustomSelect = (props) => <Select className="custom-select" {...props} />;
const baseUrl =
"https://my-json-server.typicode.com/wagnerfillio/api-json/posts";
const App = () => {
const userId = 1;
const initialValues = {
name: ""
};
const [user, setUser] = useState(initialValues);
const [defaultOptions, setDefaultOptions] = useState([]);
const [selectedQueue, setSelectedQueue] = useState([]);
useEffect(() => {
(async () => {
if (!userId) return;
try {
const { data } = await axios.get(`${baseUrl}/${userId}`);
setUser((prevState) => {
return { ...prevState, ...data };
});
const queues = data.queues.map((q) => ({
value: q.id,
label: q.name
}));
// Here there is a different result than emptiness
console.log(queues);
setDefaultOptions(queues);
} catch (err) {
console.log(err);
}
})();
return () => {
setUser(initialValues);
};
}, []);
// Here is an empty result
console.log(defaultOptions);
const handleChange = async (e) => {
const value = e.map((x) => x.value);
console.log(value);
setSelectedQueue(value);
};
return (
<div className="App">
Multiselect:
<CustomSelect
options={options}
defaultValue={defaultOptions}
onChange={handleChange}
isMulti
/>
</div>
);
};
export default App;
React不会在调用setState时立即更新状态,有时可能需要一段时间。如果您想在设置新状态后执行某些操作,可以使用 useEffect 来确定状态是否更改,如下所示:
const [ queues, setQueues ] = useState([])
useEffect(()=>{
/* it will be called when queues did update */
},[queues] )
const someHandler = ( newValue ) => setState(newValue)
setState 的闭包和异步性质
您正在经历的是 closures(渲染期间如何在函数内捕获值)和 setState 的异步性质的组合。
请参阅此Codesandbox以获取工作示例
考虑这个测试组件
const TestComponent = (props) => {
const [count, setCount] = useState(0);
const countUp = () => {
console.log(`count before: ${count}`);
setCount((prevState) => prevState + 1);
console.log(`count after: ${count}`);
};
return (
<>
<button onClick={countUp}>Click Me</button>
<div>{count}</div>
</>
);
};
测试组件是您用来说明闭包和 setState 异步性质的简化版本,但这些想法可以推断到您的用例。
渲染组件时,每个函数都会创建为闭包。考虑第一次渲染时的函数
countUp
。由于 count 在 useState(0)
中初始化为 0,因此将所有 count
实例替换为 0,看看它在初始渲染的闭包中是什么样子。
const countUp = () => {
console.log(`count before: ${0}`);
setCount((0) => 0 + 1);
console.log(`count after: ${0}`);
};
记录设置计数之前和之后的计数,可以看到设置计数之前和“设置”计数之后两个日志都会显示0。
setCount 是异步的,这基本上意味着:调用 setCount 会让 React 知道它需要安排渲染,然后它将修改 count 的状态并使用下一个渲染上的 count 值更新闭包。
因此,初始渲染将如下所示
const countUp = () => {
console.log(`count before: 0`);
setCount((0) => 0 + 1);
console.log(`count after: 0`);
};
当调用 countUp 时,该函数将记录创建该函数闭包时
count
的值,并让 React 知道它需要重新渲染,因此控制台将如下所示
count before: 0
count after: 0
React 将重新渲染并因此更新
count
的值,并重新创建 countUp 的闭包,如下所示(替换 count 的值)。然后,这将使用最新的 count 值更新任何可视组件,以显示为 1
const countUp = () => {
console.log(`count before: 1`);
setCount((1) => 1 + 1);
console.log(`count after: 1`);
};
并且每次单击计数按钮时都会继续这样做。
这是来自 codeSandbox 的片段。请注意控制台如何从初始渲染关闭控制台日志中记录
0
,但由于 UI 的异步渲染,单击一次后 count
的显示值显示为 1
。
如果您希望查看该值的最新渲染版本,最好使用 useEffect 来记录该值,一旦调用 setState,这将在 React 的渲染阶段发生
useEffect(() => {
console.log(count); //this will always show the latest state in the console, since it reacts to a change in count after the asynchronous call of setState.
},[count])
添加其他答案:
在类组件中,您可以在添加新状态后添加回调,例如:
this.setState(newStateObject, yourcallback)
但在函数组件中,您可以在某些值更改后调用“回调”(不是真正的回调,而是某种形式),例如
// it means this callback will be called when there is change on queue.
React.useEffect(yourCallback,[queue])
.
.
.
// you set it somewhere
setUserQueues(newQueues);
你就可以开始了。
别无选择(除非你想
Promise
)但是React.useEffect
您需要在
useEffect
挂钩内使用参数,并仅在进行某些更改时重新渲染。下面是一个带有 count
变量的示例,并且仅当计数值发生变化时钩子才会重新渲染。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
问题是,await api.get() 将返回一个承诺,因此当 setUserQueues(queues); 行时,常量数据不会拥有其数据集。正在运行。
你应该这样做:
api.get(`/users/${userId}`).then(data=>{
setUser((prevState) => {
return { ...prevState, ...data };
});
const queues = data.queues.map((q) => ({
value: q.id,
label: q.name,
}));
setUserQueues(queues);
console.log(queues);
console.log(userQueues);});
如果您想更新状态并在同一函数中使用更新后的值,那么您可以在设置状态时将值存储在变量中。
const [count, setCount] = useState(0)
const incrementCount = () => {
let updatedCount;
setCount((prevCount) => {
const newCount = prevCount + 1;
updatedCount = newCount;
return newCount; // Return the new value
})
console.log(updatedCount)
};