简体   繁体   English

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

[英]React/react hooks: child component is not re-rendering after the state is changed?

I am writing a code in react/react hooks that attempt to do the following.我正在尝试执行以下操作的 react/react 钩子编写代码。

Get an array of objects from the parent component as a prop Set it as a state using useState hook.从父组件获取一组对象作为 prop 使用useState钩子将其设置为状态。 Sort the state according to the intended filters (Time and Rating), and re-render the child component.根据预期的过滤器(时间和评级)对状态进行排序,并重新渲染子组件。 What I see is that the below code updates the state after sorting, but even though the state is updated, the child component that is dependent on the state would not re-render.我看到的是,下面的代码在排序后更新了状态,但是即使状态更新了,依赖于该状态的子组件也不会重新渲染。 I thought the child component would automatically re-render whenever the state is changed?我认为子组件会在状态改变时自动重新渲染?

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;

What might be the reason for this?这可能是什么原因? Is there something wrong with the code, or is sorting the state not considered powerful enough for React to re-render the child component?代码是否有问题,或者对状态进行排序是否被认为不足以让 React 重新渲染子组件? If latter is the case, what can be done to force re-render after sorting?如果是后者,如何在排序后强制重新渲染?

Any advice?有什么建议吗? Thanks!谢谢!

array::sort 数组::排序

The sort() method sorts the elements of an array in place and returns the sorted array. sort()方法就地对数组的元素进行排序并返回排序后的数组。 The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.默认排序顺序是升序,建立在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列的基础上。

What this means for you is the order of elements stored in an array may change, but the array is sorted in place which means the same array reference is returned (unlike other array functions that return new arrays).这对您来说意味着存储在数组中的元素的顺序可能会改变,但数组是原位排序的,这意味着返回相同的数组引用(与返回数组的其他数组函数不同)。

React reconciliation occurs by examining state and props and makes a holistic assumption that if the next state/prop references haven't changed then the values haven't changed and thus returns the last computed rendered DOM. React 协调通过检查 state 和 props 发生,并做出一个整体假设,如果下一个 state/prop 引用没有改变,那么值没有改变,因此返回最后计算的渲染 DOM。 This is the important detail of updating react state... each update needs to reference a new object.这是更新反应状态的重要细节……每次更新都需要引用一个新对象。

In your case you are merely saving the reference to the current array in state, mutating it, and resaving it.在你的情况你只是保存参考当前的数组中的状态,变异它,并重新保存它。 Since the reference is stable and doesn't change, react doesn't re-render.由于引用是稳定的并且不会改变,所以 react 不会重新渲染。

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

The correct react way is to copy the current array values into a new array so it will have a new object reference.正确的反应方式是将当前数组值复制一个数组中,这样它就会有一个新的对象引用。

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 checks for changes in props and state by doing a shallow object equality check. React 通过执行浅层对象相等性检查来检查 props 和 state 的变化。 If you set the state to the same object you received from state, React assumes you aborted your change and doesn't do anything, even if the properties on the object changed.如果你将 state 设置为你从 state 接收到的同一个对象,React 假设你中止了你的更改并且不做任何事情,即使对象上的属性发生了变化。

The key is that the sort() method sorts the array in place, and returns a reference to the same array.关键是sort()方法对数组进行原地排序,并返回对同一数组的引用。 So React sees it as the same array object, even though the orser of its entries is different.因此 React 将其视为相同的数组对象,即使其条目的顺序不同。

The solution is to create a new array:解决方案是创建一个新数组:

let k = [...newarray];

When k is passed to setnewarray , React sees that it is an entirely different object, and triggers the rerender.k传递给setnewarray ,React 认为它是一个完全不同的对象,并触发重新渲染。

Sort() work in place, which means you won't get what you want via directly return. Sort()就地工作,这意味着您不会通过直接返回获得您想要的东西。

By the way, you can write the sort() in a better format as below顺便说一句,您可以以更好的格式编写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);
};

Try it online here:在这里在线试用:

编辑 kind-saha-qfjpg

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

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