React/react hooks:子组件在状态改变后不重新渲染?

IT技术 javascript reactjs sorting react-hooks
2021-04-26 15:08:01

我正在尝试执行以下操作的 react/react 钩子编写代码。

从父组件获取一组对象作为 prop 使用useState钩子将其设置为状态根据预期的过滤器(时间和评级)对状态进行排序,并重新渲染子组件。我看到的是,下面的代码在排序后更新了状态,但是即使状态更新了,依赖于该状态的子组件也不会重新渲染。我认为子组件会在状态改变时自动重新渲染?

import React, {useState} from 'react';
import ProfilePostspreview from './ProfilePostspreview';

function ProfileNavigation(props){
   const [newarray, setnewarray]=useState(props.parray);   //'parray' object passed as props and saved as 'newarray' state    
   const otc = () => {  //Function to sort the state by time and set a new state
       let k=newarray;
       setnewarray(k.sort((a, b) => (a.time > b.time) ? -1 : 1 ));
   }
   const orc = () => {  //Function to sort the state by rating and then time and set a new state
       let k=newarray;
       setnewarray(k.sort((a, b) => (a.rating > b.rating) ? -1 : (a.rating === b.rating) ? ((a.time > b.time) ? -1 : 1) : 1 ));
   }

return (
   <div>
      <div className="sm_options">    //Component to trigger the otc and orc functions
          <div className="sm_button" id="order_t" onClick={otc}>Sort by time</div>    
          <div className="sm_button" id="order_r" onClick={orc}>Sort by rating</div>    
      </div>
      <div className="posts_preview_columns_p"> //This is dependent on the 'newarray' state but is not re-rendering even after the state is sorted and updated?
      {newarray.map(na=>
      <ProfilePostspreview
          postThumbnail={na.photolink}
          rating={na.rating}
          time={na.time}
          target={na.target}
      />
      )}
      </div>
   </div>
);
}

export default ProfileNavigation;

这可能是什么原因?代码是否有问题,或者对状态进行排序是否被认为不足以让 React 重新渲染子组件?如果是后者,如何在排序后强制重新渲染?

有什么建议吗?谢谢!

3个回答

数组::排序

sort()方法对数组的元素进行原地排序并返回排序后的数组。默认排序顺序是升序,建立在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列的基础上。

这对您来说意味着存储在数组中的元素的顺序可能会改变,但数组是原位排序的,这意味着返回相同的数组引用(与返回数组的其他数组函数不同)。

React 协调通过检查 state 和 props 发生,并做出一个整体假设,如果下一个 state/prop 引用没有改变,那么值没有改变,因此返回最后计算的渲染 DOM。这是更新react状态的重要细节……每次更新都需要引用一个新对象。

在你的情况你只是保存在参考当前阵列中的状态,变异它,并重新保存它。由于引用是稳定的并且不会改变,react 不会重新渲染。

const otc = () => {
  let k = newarray; // <-- saved array reference!!
  setnewarray(k.sort((a, b) => (a.time > b.time) ? -1 : 1 ));
}

正确的react方式是将当前数组值复制一个数组中,这样它就会有一个新的对象引用。

const otc = () => {
  const newSortedArray = [...newArray].sort(
    (a, b) => (a.time > b.time) ? -1 : 1
  ); // spread old array values into new array, then sort
  setNewArray(newSortedArray);
}

React 通过执行浅层对象相等性检查来检查 props 和 state 的变化。如果你将 state 设置为你从 state 接收到的同一个对象,React 假设你中止了你的更改并且不做任何事情,即使对象上的属性发生了变化。

关键是该sort()方法对数组进行原地排序,并返回对同一数组的引用。因此 React 将其视为同一个数组对象,即使其条目的顺序不同。

解决方案是创建一个新数组:

let k = [...newarray];

k传递给 时setnewarray,React 会看到它是一个完全不同的对象,并触发重新渲染。

Sort() 就地工作,这意味着您不会通过直接返回获得您想要的东西。

顺便说一句,你可以用sort()更好的格式编写如下

const otc = () => {
  const result = [...newarray];
  result.sort((a, b) => b.time - a.time);
  setNewarray(result);
};
const orc = () => {
  const result = [...newarray];
  result.sort((a, b) =>
    a.rating !== b.rating ? b.rating - a.rating : b.time - a.time
  );
  setNewarray(result);
};

在这里在线试用:

编辑 kind-saha-qfjpg