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;
答案 0 :(得分:3)
React 在调用 setState 时不会立即更新状态,有时可能需要一段时间。如果你想在设置新状态后做一些事情,你可以使用 useEffect 来确定状态是否像这样改变:
const [ queues, setQueues ] = useState([])
useEffect(()=>{
/* it will be called when queues did update */
},[queues] )
const someHandler = ( newValue ) => setState(newValue)
答案 1 :(得分:2)
添加到其他答案:
在类组件中,您可以在添加新状态后添加回调,例如:
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
答案 2 :(得分:1)
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
。由于 useState(0)
中的 count 被初始化为 0,因此将所有 count
实例替换为 0,以查看它在初始渲染的闭包中的外观。
const countUp = () => {
console.log(`count before: ${0}`);
setCount((0) => 0 + 1);
console.log(`count after: ${0}`);
};
设置count前后的Logging count,可以看到设置count前和设置count后两个log都会显示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
。< /p>
如果你想看到值的最新渲染版本,最好使用 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])
答案 3 :(得分:0)
您需要在 useEffect
钩子中使用一个参数,并且只有在进行了一些更改时才重新渲染。下面是一个带有 count
变量的示例,并且仅当计数值发生变化时钩子才会重新渲染。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
答案 4 :(得分:0)
问题是 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);});