React的setState()不会触发重新渲染。

4

我有一个组件,它从其他文件接收数据并将其设置到状态中:

const [sortedPlans, setSortedPlans] = useState(
    data.Plans.sort((a, b) => b.IndustryGrade - a.IndustryGrade)
  );//data is from external file

在设置状态、排序数据并呈现初始屏幕后,我有一个函数,每次调用时都会对sortedPlans进行排序:

const sort = (event) => {
    console.log(event);
    const newArr = sortedPlans.sort((a, b) => {
      return b[event] - a[event];
    });
    console.log(newArr);
    return setSortedPlans(newArr);
  };

问题在于这样永远不会触发组件的重新渲染。我希望当我设置新状态时,能够在jsx中看到它。为什么当我console.log(newArr)时,结果被正确排序,但是这个状态设置没有触发重新渲染?下面是sandbox链接:https://codesandbox.io/s/charming-shape-r9ps3p?file=/src/App.js

6
只有当你设置一个新值时,React 才会重新渲染。但是 sortedPlans.sort 会原地对数组进行排序并返回相同的数组。 - Felix Kling
这是一个类似的问题 https://dev59.com/sbroa4cB1Zd3GeqPoaRI - rMonteiro
@FelixKling 所以基本上我正在设置与React假定这是相同数组的相同内容。 - Borislav Stefanov
2
这是同一个数组:var arr = [3,2,1]; console.log(arr === arr.sort());。React 只会在必要时重新渲染,即如果值发生变化。React 不知道您是否改变了此数组。这就是为什么在 React 中,通常在修改可变值之前创建其副本的原因。 - Felix Kling
这是关于对象引用的问题。请查看此问题 https://github.com/facebook/react/issues/19181 - Jeff Pal
1个回答

19

请看这里:Codesandbox 演示

你应该首先创建一个要修改的数组的浅拷贝,然后将该数组设置为新的状态。这样它就会重新渲染组件,你就能按照你想要的方式进行过滤了。

  const sort = (event) => {
    console.log(event);
    //shallow copying the state. 
    const newArr = [...sortedPlans];

    //modifying the copy
    newArr.sort((a, b) => {
      return b[event] - a[event];
    });
    console.log(newArr); //results here are correctly sorted as per event
    
    //setting the state to the copy triggers the re-render.
    return setSortedPlans(newArr);
  };

1
你是对的。我会接受这个答案,但我们能解释一下为什么需要吗? - Borislav Stefanov
2
React组件会在其状态或属性发生变化时自动重新渲染。在您的示例中,sortedPlans.sort正在就地对数组进行排序并返回相同的数组,因此您实际上从未更新状态。最简单的方法是复制状态,修改副本,然后将状态设置为副本,然后状态得到更新并且组件重新渲染。 - Reinier68
4
React 会使用严格比较 === 来与之前的状态进行比较,以决定是否重新渲染。对于相同的对象(或数组,在技术上也是对象),=== 将返回 true。在变异之后,对象(或数组)在技术上仍然相同,尽管经过了变异(即更改了字段)或按照您的情况进行了排序。事实上,arr = []; arr.sort(); arr === arr - Igor Loskutov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接