React Child 在操作 child 中的道具时正在更新 Parent State。不需要的

React Child is updating Parent State when manipulating the props in child. Unwanted

以下代码已在 https://codesandbox.io/s/child-updating-parent-dont-want-pfdxd 上线 我面临的问题是,当我对子组件 (page2) 进行排序时,它还会更新父组件 (App) 的状态。我想避免状态更新,因为另一个子组件(第 1 页)应该显示未排序的数据。我考虑过在 App.js 上有两个具有相同数据(两个名字和两个数字)的州。然后为第 1 页使用一组状态(名称和数字),为 page2.This 使用另一组状态似乎是多余和不必要的,但我不确定在第 2 页上排序时如何避免状态更新。有没有办法在子组件中操作数据时避免状态更新?

/*Parent*/  
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
          names: ["john", "sally", "bill", "rebecca"]
        };
      }
      render() {
        const { numbers, names } = this.state;
        return (
          <Router>
            <div className="App">
              <Header />
              <Switch>
                <Route
                  path="/page1"
                  exact
                  render={(routeProps) => (
                    <Page1 digits={numbers} people={names} {...routeProps} />
                  )}
                />
                <Route
                  path="/page2"
                  exact
                  render={(routeProps) => (
                    <Page2 digits={numbers} people={names} {...routeProps} />
                  )}
                />
              </Switch>
            </div>
          </Router>
        );
      }
    }
    export default App;

这是接收道具的第一个组件

/*child 1*/
const page1 = ({ people, digits }) => {
  return (
    <div>
      <h1>
        Number Should Be "1" And Name Should Be "john" On This Page Regardless
        Of Sort On Page 2
      </h1>

      <p>Number: {digits[0]}</p>
      <p>Name: {people[0]}</p>
    </div>
  );
};
export default page1;

这是接收道具的第二个组件

/*child 2*/

    const Page2 = ({ people, digits }) => {
      /* when uncommented the sort will also change state on App.js --- 
      I want state on App.js to remain in original state aft sorting*/
    
      /* --- UNCOMMENT BELOW TO SORT --- */
    
      /*people.sort((a, b) => b.toLowerCase().localeCompare(a.toLowerCase()));
      digits.sort((a,b)=>b-a)*/
    
      return (
        <div>
          <h1>
            Number Should Be "10" And Name Should Be "sally" On This Page When Sort
            Is Uncommented
          </h1>
          <p>Number: {digits[0]}</p>
          <p>Name: {people[0]}</p>
        </div>
      );
    };
    export default Page2;

在Javascript中,sort方法执行就地排序。因此,当您执行 people.sort 时,您实际上最终修改了传递的 people 道具。由于数组是通过引用传递的,因此 people 的所有其他引用都将显示新的排序值。

要解决此问题,您应该改变传递的 prop 的本地副本以防止此类意外结果。

const Page2 = ({ people, digits }) => {

  const sortedPeople = [...people].sort((a, b) => b.toLowerCase().localeCompare(a.toLowerCase()))
  
  const sortedDigits = [...digits].sort((a,b)=>b-a);

  return (
    <div>
      <h1>
        Number Should Be "10" And Name Should Be "sally" On This Page When Sort
        Is Uncommented
      </h1>
      <p>Number: {sortedDigits[0]}</p>
      <p>Name: {sortedPeople[0]}</p>
    </div>
  );
};
export default Page2;

为了进一步解释上述方法的原理,

我们现在不是直接改变 prop,而是创建数组的副本,在本例中是 peopledigits 通过使用像这样的 ES6 扩展运算符 [...people] 和在这个新副本上排序。

const sortedPeople = [...people].sort((a, b) => b.toLowerCase().localeCompare(a.toLowerCase()))

PS。您也可以执行 Array.from(people)people.slice() 以获得相同的结果。

请记住,您可以使用 useMemo 挂钩进一步优化 React 中的渲染性能,并执行类似的操作

const sortedPeople = useMemo(() => [...people].sort((a, b) => b.toLowerCase().localeCompare(a.toLowerCase())), [people]);