[英]How can I avoid scoping problems related to React hooks
I have a recurring problem that seems to happen whenever I try and do anything remotely complicated and async using hooks.我有一个反复出现的问题,每当我尝试使用钩子做任何远程复杂和异步的事情时似乎都会发生。
The timeline which causes the issue goes something like this:导致问题的时间线是这样的:
s
在钩定义一些状态s
d
and use it to set state s
进行异步调用以获取一些数据d
并使用它来设置状态s
d
, and use it to update s
基于d
进行后续调用,并使用它来更新s
The problem is that at 3.;问题是在 3.; when I want to update the state s
, it at that point is the s
as it was defined in the scope at which the function was defined, ie.当我想更新状态s
,它是在定义函数的范围内定义的s
,即。 it has no concept of the latest updated state, only that which it knew about when the function was defined.它没有最新更新状态的概念,只有它在定义函数时知道的状态。
I have been able to get around this using a few things, a couple of things which tend to work:我已经能够使用一些东西来解决这个问题,一些东西往往有效:
setTimeout
to force the update to happen in the next render cycle使用setTimeout
强制更新发生在下一个渲染周期These aren't always (or ever) either good or desirable solutions.这些并不总是(或永远)不是好的或理想的解决方案。 I'm guessing I'm missing something fundamental here but I haven't been able to find anything about this online, and I haven't yet had any viable suggestions from my colleagues.我猜我在这里遗漏了一些基本的东西,但我在网上找不到任何关于这个的东西,我还没有从我的同事那里得到任何可行的建议。
Functions defined within the body of a React functional component will be defined with the scope to which they have access at that time.在 React 功能组件的主体中定义的函数将使用它们当时可以访问的范围来定义。 This isn't always the latest state.这并不总是最新的状态。 How can I get around the problem?我怎样才能解决这个问题?
const getUsers = () => Promise.resolve({ 1: { name: 'Mr 1', favouriteColour: null, }, 2: { name: 'Ms 2', favouriteColour: null, }, }); const getFavouriteColorForUser = (id) => Promise.resolve({ id, color: Math.random() > 0.5 ? 'red' : 'blue' }); const App = () => { const [users, setUsers] = React.useState({}); const handleClick = () => { getUsers() .then(data => { setUsers(data); return Promise.resolve(data); }) .then(data => { return Promise.all(Object.keys(data).map(getFavouriteColorForUser)); }) .then(data => { const updatedUsers = { ...users, ...data.reduce( (p, c) => ({ ...p, [c.id]: { ...users[c.id], favouriteColor: c.color }, }), {} ) }; console.log('users:'); console.dir(users); console.log('color data:'); console.dir(data); console.log('updatedUsers:'); console.dir(updatedUsers); setUsers(updatedUsers); }) } return ( <div> {Object.keys(users).map(k => users[k]).map(user => ( <div>{user.name}'s favourite colour is {user.favouriteColour}</div> ))} <button onClick={handleClick}>Get Users</button> </div> ); } ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="app"></div>
setState has a variant where you pass it a function. setState 有一个变体,您可以在其中传递一个函数。 That function is guaranteed to be called with the most recent value from the state.保证使用状态中的最新值调用该函数。 You can use that to calculate your next state:你可以用它来计算你的下一个状态:
.then(data => {
setUsers(previousUsers => {
const updatedUsers = {
...previousUsers, // <--- using previousUsers, not users
...data.reduce(
(p, c) => ({
...p,
[c.id]: {
...previousUsers[c.id], // <--- using previousUsers, not users
favouriteColor: c.color
},
}), {})
};
return updatedUsers;
})
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.