状态在 useEffect 中没有更新,为啥?
Posted
技术标签:
【中文标题】状态在 useEffect 中没有更新,为啥?【英文标题】:State is not updating in useEffect why?状态在 useEffect 中没有更新,为什么? 【发布时间】:2022-01-16 17:55:00 【问题描述】:我正在尝试制作一个加密价格跟踪网站。我在每 10 秒更新一次 coinList 变量 prevData 变量后获取数据。我也想访问 useEffect 中的 prevData 变量,但在其中变量没有更新,它的值仍然是 []。但是,如果我在 useEffect 之外使用 prevData,数据会发生变化,我可以使用它。
我只想要 prevData 在 useEffect 中更新的值。
function App()
const [coinsList, setCoinsList] = useState([]);
const [prevData, setPrevData] = useState([]);
let [width, setWidth] = useState(window.innerWidth)
useEffect(() =>
async function fetchData()
const response = await fetch('api key').then(res => res.json())
setCoinsList(res);
setPrevData(res);
fetchData()
async function fetchData2()
const res = await fetch('api key').then(res => res.json())
for (let i = 0; i < prevData.length; i++)
if (res[i].current_price > prevData[i].current_price)
let coin = document.getElementById(`$prevData[i].id`)
coin.classList.add('green')
console.log(coin)
// setTimeout(() =>
// coin.classList.remove('green')
// , 1000)
else if (res[i].current_price < prevData[i].current_price)
let coin = document.getElementById(`$prevData[i].id`)
coin.classList.add('red')
console.log(coin)
// setTimeout(() =>
// coin.classList.remove('red')
// , 1000)
else
let coin = document.getElementById(`$prevData[i].id`)
console.log(coin)
coin.classList.add('blue')
// setTimeout(() =>
// coin.classList.remove('blue')
// , 1000)
setPrevData(res);
setCoinsList(res);
setInterval(() =>
fetchData2()
console.log(prevData) // it is not updating
, 10000)
, [])
useEffect(() =>
window.addEventListener('resize', () =>
setWidth(window.innerWidth)
)
, [window.innerWidth])
console.log(prevData) // it is updating
【问题讨论】:
【参考方案1】:在fetchData2
中,您拥有prevData
状态的陈旧外壳。它在区间回调范围内被关闭。
一种补救方法是使用 React ref 和一个额外的 useEffect
挂钩来缓存可以在 fetchData2
回调中访问的 prevData
状态的副本。
function App()
const [coinsList, setCoinsList] = useState([]);
const [prevData, setPrevData] = useState([]);
const prevDataRef = React.useRef(prevData);
useEffect(() =>
prevDataRef.current = prevData;
, [prevData]);
...
useEffect(() =>
...
async function fetchData2()
const prevData = prevDataRef.current; // <-- grab current value
const res = await fetch('api key').then(res => res.json());
for (let i = 0; i < prevData.length; i++)
if (res[i].current_price > prevData[i].current_price)
let coin = document.getElementById(`$prevData[i].id`);
coin.classList.add('green');
else if (res[i].current_price < prevData[i].current_price)
let coin = document.getElementById(`$prevData[i].id`);
coin.classList.add('red');
else
let coin = document.getElementById(`$prevData[i].id`);
coin.classList.add('blue');
setPrevData(res);
setCoinsList(res);
setInterval(() =>
fetchData2();
, 10000)
, []);
【讨论】:
感谢您的帮助 现在我的代码可以按照我的意愿完美运行。但是你能解释一下为什么它不起作用吗?我没有得到 prevData 正在关闭,它在 setinterval 上关闭 - @Nimish 正确,初始prevData
值在 setInterval
回调范围内被关闭。这意味着您创建一个匿名回调函数并将其传递给setInterval
。这是 that 函数的特定副本或“实例”,其中复制了所有使用的变量。此间隔回调的相同副本/实例被重复使用且永不更新,因此内部复制的变量也永不更新。 React ref 是一个外部(回调范围)可变的对象。
这意味着我们在setInterval
回调函数中添加的任何东西,无论它当时的状态是什么,它只会重复那个状态,不会根据原来的状态更新?
@Nimish Affirmative.【参考方案2】:
您在setInterval
范围内的prevData
值上有closure,您可以使用引用来修复它:
const prevDataRef = useRef();
// Keep the ref updates
useEffect(() =>
prevDataRef.current = prevData;
, []);
// no closure
useEffect(() =>
setInterval(() =>
fetchData2(); // use prevDataRef.current
console.log(prevDataRef.current); // updated
, 10000);
, []);
【讨论】:
以上是关于状态在 useEffect 中没有更新,为啥?的主要内容,如果未能解决你的问题,请参考以下文章
状态更新没有立即反映。如何使用 useEffect 的依赖数组来显示更改?
为啥 useEffect 不能在 return 语句中访问我的状态变量?
为啥 useState 钩子在 useEffect() 中使用循环时不更新