简体   繁体   English

为什么 react 不更新“then” promise 中的 state?

[英]Why react doesn't update state in “then” promise?

I have a table with Delete button in each row.我在每一行都有一个带有Delete按钮的表格。 When you delete the row, the Client will send request do the API asynchronously.当您删除该行时,客户端将异步发送执行 API 的请求。 Which means, that if you delete multiple rows quickly, you don't have to wait for one request to finish, to send another Delete request.这意味着,如果您快速删除多行,您不必等待一个请求完成,再发送另一个删除请求。 When you click Delete button and request was send, the button will be disabled (before the response comes back and the row will disappear).当您单击Delete按钮并发送请求时,该按钮将被禁用(在响应返回之前该行将消失)。

This is the function that handles delete in the Container:这是在容器中处理删除的 function:

  const [deletingTeams, setDeletingTeams] = useState([]);

  const handleDelete = (teamId) => {
    setDeletingTeams([...deletingTeams, teamId]);

    deleteTeam(teamId).then(() => {    
      console.log(deletingTeams); <---- This prints EMPTY array. Why????   
      let newState = deletingTeams.filter((id) => id !== teamId);
      setDeletingTeams(newState);
    });
  };

Very simple.很简单。 deletingTeams is an array that holds ids of teams that are being deleted at the moment (the response didn't comeback from that API, that deletion was successful). deletingTeams是一个数组,其中包含当前正在被删除的团队的 ID(响应没有从 API 恢复,删除成功)。

In line 3 ( setDeletingTeams([...deletingTeams, teamId]); ) I'm adding new teamId to the array, and it works.在第 3 行( setDeletingTeams([...deletingTeams, teamId]); )我将新的 teamId 添加到数组中,它可以工作。 New array is being passed to the List component and Delete button is indeed disabled.新数组正在传递给List组件,并且Delete按钮确实被禁用。 BUT ...但是...

...when response comes back, in then promise, I want to remove that teamId from the array. ...当响应返回时,在then中,我想从数组中删除该teamId The problem is, that deletingTeams array is already empty (before the filter).问题是, deletingTeams数组已经为空(在过滤器之前)。 Why??为什么??

Thanks for the explanation,感谢您的解释,

State updates are async - you are not guaranteed to see latest value of the updated state straight away. State 更新是异步的 - 您不能保证立即看到更新后的 state 的最新值。

Use the callback approach to filter and set the state.使用回调方法过滤和设置 state。

const [deletingTeams, setDeletingTeams] = useState([]);

const handleDelete = (teamId) => {
  setDeletingTeams([...deletingTeams, teamId]);

  deleteTeam(teamId).then(() => {
    // console.log(deletingTeams); //<---- state updates are async - you will see the latest value of the state in the next re-render
    // let newState = deletingTeams.filter((id) => id !== teamId); //<---- remove this
    setDeletingTeams((prev) => prev.filter((id) => id !== teamId)); //<--- use call back approach to set the state
  });
};

In short, the reason this is happening is because deletingTeams gets its value before your asynchronous call.简而言之,发生这种情况的原因是因为deletingTeams在您的异步调用之前获得了它的值。 The reasons why are subtle!原因很微妙!

What's going on is, when you use detetingTeams in the then function for deleteTeam , the callback function you pass in is using a JavaScript feature called function closures . What's going on is, when you use detetingTeams in the then function for deleteTeam , the callback function you pass in is using a JavaScript feature called function closures . The variable deletingTeams is used in your then function - but it's not defined by that function.变量 deleteTeams 用于您thendeletingTeams - 但它不是由 function 定义的。 So it can't have local function scope, Where exactly is it coming from?所以不能有本地的function scope,到底是从哪里来的? then?然后?

The answer is nuanced.答案很微妙。 The variable is defined in your first line: const [deletingTeams, setDeletingTeams] = useState([]);该变量在您的第一行中定义: const [deletingTeams, setDeletingTeams] = useState([]); . . At this point, the variable is assigned a value based on your components' current state - the teams you currently have before deleting.此时,根据您的组件的当前 state(删除前您当前拥有的团队)为变量分配一个值。 Then, you reference it in your function.然后,您在 function 中引用它。 At this time, the variable deletingTeams becomes added to the then functions' scope.此时,变量deletingTeams被添加到then函数的 scope 中。 It is a reference to the same variable outside the function!它是对函数外部相同变量的引用!

Ok, so the reason that deletingTeams is an empty array is because it's an empty array when you create the then function, right?好的,所以deletingTeams是一个空数组的原因是因为它是一个空数组,当你创建then时,对吧? Not quite.不完全的。 Any changes to deletingTeams are reflected when you execute the then function.当您执行then时,将反映对deletingTeams团队的任何更改。 But in your example, deletingTeams never changes its value.但在您的示例中, deletingTeams永远不会改变它的值。 Your component's state changes when you call setDeletingTeams , but you only look up the state once - at the beginning of the block of code.当您调用setDeletingTeams时,您的组件的 state 会发生变化,但您只需在代码块的开头查找 state 一次。 There's no magic that will change deletingTeams ' value;没有什么魔法可以改变deletingTeams的价值; for that, you have to call useState again.为此,您必须再次调用useState

deletingTeams is given a fresh value each time your component rerenders.每次您的组件重新呈现时,都会为deletingTeams赋予一个新值。 But unfortunately, the then function's closure scope is only determined once.但不幸的是, then函数的闭包 scope 只确定了一次。 (Each time you rerender, you actually generate a new then function, scoped to that render's version of the deletingTeams variable,) The component may have rerendered, but the then block is still called with the same stale value. (每次重新渲染时,实际上都会生成一个新的then function,范围为该渲染版本的deletingTeams变量,)组件可能已重新渲染,但仍使用相同的陈旧值调用then块。

So, altogether, here's what's going on:所以,总的来说,这就是发生的事情:

  1. deletingTeams is assigned a value in your first line: const [deletingTeams, setDeletingTeams] = useState([]);在您的第一行中为deletingTeams团队分配了一个值: const [deletingTeams, setDeletingTeams] = useState([]);
  2. You update the state with setDeletingTeams , but useState is only called once, so deletingTeams is never updates to match new values.您使用 setDeletingTeams 更新setDeletingTeams ,但useState只调用一次,因此deletingTeams永远不会更新以匹配新值。
  3. deletingTeams gets a new value on each new render of your component, but by then, your then function is unfortunately already bound to a specific, past deletingTeams variable that never updates. deletingTeams在您的组件的每个新渲染上都会获得一个新值,但是到then ,您的deletingTeams不幸已经绑定到一个特定的、过去的从不更新的 deleteTeams 变量。
  4. So when your then block executes, deletingTeams has a stale value.因此,当您的then块执行时, deletingTeams具有陈旧的值。

So what is the correct approach, then?那么正确的做法是什么呢? We can read the state in the then function to get the freshest value of deletingTeams .我们可以在 function 中读取then以获取deletingTeams团队的最新值。 It's simply this.state.deletingTeams .就是this.state.deletingTeams Here's more information: https://reactjs.org/docs/hooks-state.html#reading-state这里有更多信息: https://reactjs.org/docs/hooks-state.html#reading-state

(Because you're using arrow functions, the then function automatically uses the same this as the component, so everything should work.) (因为您使用的是箭头函数, then function 自动使用与组件相同的this ,所以一切都应该工作。)

So this should do the trick:所以这应该可以解决问题:

  const [deletingTeams, setDeletingTeams] = useState([]);

  const handleDelete = (teamId) => {
    setDeletingTeams([...deletingTeams, teamId]);

    deleteTeam(teamId).then(() => {    
      let newState = this.state.deletingTeams.filter((id) => id !== teamId);
      setDeletingTeams(newState);
    });
  };

(Be sure to use this.state.deletingTeams instead of calling useState again in this kind of situation. Calling useState inside of nested functions or callbacks can lead to bugs: https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level ) (请务必使用this.state.deletingTeams而不是在这种情况下再次调用useState 。在嵌套函数或回调中调用useState可能会导致错误: https://reactjs.org/docs/hooks-rules.html#仅在顶层调用挂钩

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM