React子组件未使用处理程序属性和自定义排序方法更新父组件的状态

huangapple go评论59阅读模式
英文:

React child not updating state of parent using handler prop and custom sort method

问题

以下是代码的翻译部分:

// 我有以下的父组件。它使用了 `dynamicSort`,这是一个自定义排序函数,用于对对象数组进行排序,按指定的键返回已排序的对象数组。我已确认排序函数是有效的:

import React, { useState, useEffect } from 'react';
import { dynamicSort } from '../helpers';
import Container, ChildHeader, ChildBody, Spinner, ...

const Parent = () => {
  const [listItems, setListItems] = useState([]);

  useEffect(() => {
    // 获取列表项并在组件挂载时设置它们
    // 这部分目前是有效的
  }, [])

  const sortList = column => {
    setListItems(prev => dynamicSort(prev, column, 'asc'));
  };

  return (
    <Container>
        <ChildHeader
          sortList={sortList}
        />
        <ChildBody>
          {listItems.length > 0 ? (
            listItems.map(item => {
              return (
                <ChildRow
                  key={item.id}
                  rowItem={item}
                />)
            })
          ) : (
            <Spinner />
          )}
        </ChildBody>
    </Container>
  );
};

export default Parent;

ChildHeader 组件内,我通过以下方式调用 sortList 属性来处理标题的点击:

const ChildHeader = ({ sortList }) => {
    return (
      <HeaderContainer>
        <Column1
          onClick={() => {
            sortList('Column1'); // 这应该对父组件中的列表进行排序并重置状态
          }}
        >
          {'列标签'}
        </Column1>
        // 更多列...
      </HeaderContainer>
    );
};

export default ChildHeader;

我已确认当子组件被点击时,处理程序被调用。但是由于某种原因,父组件中的状态不会更新。对我来说更奇怪的是,如果我将父组件中的 sortList 方法更改为以下代码,那么在子组件点击时列表会更新:

const sortList = column => {
    setListItems(prev => prev.slice(0, 3)); // 使用 slice 而不是我的自定义排序方法
};

这让我觉得我的排序方法可能有问题。我已确认排序方法确实返回了按键正确排序的对象数组。有没有人知道发生了什么?我将包含排序方法供参考:

export const dynamicSort = (list, column, direction) => {
  const compare = (a, b) => {
    switch (column) {
      case 'Column1':
        if (direction === 'asc') {
          if (a.key1 > b.key1) {
            return 1;
          }
          if (a.key1 < b.key1) {
            return -1;
          }
          return 0;
        }
        if (direction === 'desc') {
          if (a.key1 < b.key1) {
            return 1;
          }
          if (a.key1 > b.key1) {
            return -1;
          }
          return 0;
        }
      // 更多情况以下面的方式处理

      default:
        return null;
    }
  };
  return list.sort(compare); // 这确实返回了按键正确排序的对象数组
}
英文:

I have the following parent component. It makes use of dynamicSort, a custom sort function for an array of objects that returns a sorted array of objects by the specified key. I have confirmed that the sort function works:

import React, { useState, useEffect } from &#39;react&#39;;
import { dynamicSort } from &#39;../helpers&#39;;
import Container, ChildHeader, Childbody, Spinner, etc...

const Parent = () =&gt; {
  const [listItems, setListItems] = useState([]);

  useEffect(() =&gt; {
    // fetch list items and set them here on component mount
    // this works currently
  }, [])

  const sortList = column =&gt; {
    setListItems(prev =&gt; dynamicSort(prev, column, &#39;asc&#39;));
  };

  return (
    &lt;Container&gt;
        &lt;ChildHeader
          sortList={sortList}
        /&gt;
        &lt;ChildBody&gt;
          {listItems !== [] ? (
            listItems.map(item =&gt; {
              return (
                &lt;ChildRow
                  key={item.id}
                  rowItem={item}
                /&gt;)
            })
          ) : (
            &lt;Spinner /&gt;
          )}
        &lt;/ChildBody&gt;
    &lt;/Container&gt;
  );
};

export default Parent;

Inside of childHeader, I call the prop sortList on a heading click like this:

const ChildHeader = ({ sortList }) =&gt; {
    return (
      &lt;HeaderContainer&gt;
        &lt;Column1
          onClick={() =&gt; {
            sortList(&#39;Column1&#39;); // This should sort and reset the state of the list in the parent
          }}
        &gt;
          {&#39;Column Label&#39;}
        &lt;/Column1&gt;
        // More columns...
      &lt;/HeaderContainer&gt;
    );
};

export default ChildHeader;

I have confirmed that handler is called when the child component is clicked. But for some reason the state does not update on the parent list. The even weirder thing to me is that if I change the sortList method in the parent to the following code, the list updates on child click.

const sortList = column =&gt; {
    setListItems(prev =&gt; prev.slice(0, 3); // Using slice instead of my custom sort method
};

This makes me think that there is something wrong with my sort method. I have confirmed that the sort method does return an array of objects and that it is sorting correctly. Any have a clue what's happening? I'll include the sort method below just for kicks:

export const dynamicSort = (list, column, direction) =&gt; {
  const compare = (a, b) =&gt; {
    switch (column) {
      case &#39;Column1&#39;:
        if (direction === &#39;asc&#39;) {
          if (a.key1 &gt; b.key1) {
            return 1;
          }
          if (a.key1 &lt; b.key1) {
            return -1;
          }
          return 0;
        }
        if (direction === &#39;desc&#39;) {
          if (a.key1 &lt; b.key1) {
            return 1;
          }
          if (a.key1 &gt; b.key1) {
            return -1;
          }
          return 0;
        }
      // More cases following below

      default:
        return null;
    }
  };
  return list.sort(compare); // This does return an array of objects sorted correctly by key 
}

答案1

得分: 2

因为您实际上并没有更新状态。您正在更改状态。根据sort的文档

> sort()方法会在原地对数组元素进行排序,并返回对同一数组的引用,现在已经排序。

因此,您正在更新状态以相同的数组引用,这意味着React无法知道状态已被以任何方式修改。

可能最简单的方法是修改您的dynamicSort函数,以对新的数组引用进行排序,这可以通过在排序之前对数组进行解构来实现:

return [...list].sort(compare);
英文:

Because you're not actually updating state. You're mutating state. According to the documentation for sort:

> The sort() method sorts the elements of an array in place and returns the reference to the same array, now sorted.

So you're updating state to the same array reference, which means React has no way to know that state has been modified in any way.

Probably the simplest approach would be to modify your dynamicSort function to sort a new array reference, which could be as simple as destructuring the array before sorting it:

return [...list].sort(compare);

huangapple
  • 本文由 发表于 2023年3月7日 00:31:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75653391.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定