我正在尝试学习钩子和useState方法使我困惑。我以数组的形式将初始值赋给一个状态。useState中的set方法不适合我,无论是否使用扩展语法。
我已经在另一台PC上做了一个API,我正在调用并获取我想设置为状态的数据。
这是我的代码:
<div id="root"></div>
<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant
const StateSelector = () => {
const initialValue = [
{
category: "",
photo: "",
description: "",
id: 0,
name: "",
rating: 0
}
];
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
// const response = await fetch("http://192.168.1.164:5000/movies/display");
// const json = await response.json();
// const result = json.data.result;
const result = [
{
category: "cat1",
description: "desc1",
id: "1546514491119",
name: "randomname2",
photo: null,
rating: "3"
},
{
category: "cat2",
description: "desc1",
id: "1546837819818",
name: "randomname1",
rating: "5"
}
];
console.log("result =", result);
setMovies(result);
console.log("movies =", movies);
} catch (e) {
console.error(e);
}
})();
}, []);
return <p>hello</p>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
setMovies(result)和setMovies(…result)都不起作用。
我希望结果变量被推入movies数组。
关闭并不是唯一的原因。
基于useState的源代码(下面进行了简化)。在我看来,价值从来没有被立即分配。
当您调用setValue时,更新操作将被排队。在时间表生效后,只有当你到达下一个渲染时,这些更新操作才会应用到那个状态。
这意味着即使我们没有闭包问题,useState的react版本也不会马上给你新值。新的值直到下一次渲染才会存在。
function useState(initialState) {
let hook;
...
let baseState = hook.memoizedState;
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next;
do {
const action = firstUpdate.action;
baseState = action(baseState); // setValue HERE
firstUpdate = firstUpdate.next;
} while (firstUpdate !== hook.queue.pending);
hook.queue.pending = null;
}
hook.memoizedState = baseState;
return [baseState, dispatchAction.bind(null, hook.queue)];
}
function dispatchAction(queue, action) {
const update = {
action,
next: null
};
if (queue.pending === null) {
update.next = update;
} else {
update.next = queue.pending.next;
queue.pending.next = update;
}
queue.pending = update;
isMount = false;
workInProgressHook = fiber.memoizedState;
schedule();
}
还有一篇文章以类似的方式解释了上面的内容,https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8
很像扩展React创建的类组件中的. setstate()。组件或React。使用useState钩子提供的更新器的PureComponent状态更新也是异步的,并且不会立即反映出来。
此外,这里的主要问题不仅是异步性质,而且是函数基于当前闭包使用状态值,状态更新将在下一次重新呈现中反映出来,这样现有的闭包不会受到影响,但会创建新的闭包。现在,在当前状态下,钩子中的值是由现有的闭包获得的,当重新呈现发生时,闭包将根据是否再次重新创建函数而更新。
即使您添加了一个setTimeout函数,尽管超时将在重新呈现发生的一段时间后运行,但setTimeout仍将使用前一个闭包的值,而不是更新的值。
setMovies(result);
console.log(movies) // movies here will not be updated
如果你想在状态更新上执行一个动作,你需要使用useEffect钩子,就像在类组件中使用componentDidUpdate一样,因为useState返回的setter没有回调模式
useEffect(() => {
// action on update of movies
}, [movies]);
就更新状态的语法而言,setMovies(result)将用异步请求中可用的movies值替换状态中先前的movies值。
但是,如果希望将响应与先前存在的值合并,则必须使用状态更新的回调语法以及正确使用扩展语法,如
setMovies(prevMovies => ([...prevMovies, ...result]));