React:当数据混合了大写和小写字母时,表自定义排序不会排序

fykwrbwg  于 2022-10-15  发布在  React
关注(0)|答案(3)|浏览(142)

我正在尝试使用Reaction中的自定义排序功能对数据进行排序。遗憾的是,该函数仅在记录为大写字母或小写字母时对项目进行排序。它不能正确地对大小写混合的数据进行排序。这是现场直播的link
以下是代码

const useSortableData = (items, config = null) => {
  const [sortConfig, setSortConfig] = React.useState(config);

  const sortedItems = React.useMemo(() => {
    let sortableItems = [...items];
    if (sortConfig !== null) {
      sortableItems.sort((a, b) => {
        if (a[sortConfig.key] < b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? -1 : 1;
        }
        if (a[sortConfig.key] > b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? 1 : -1;
        }
        return 0;
      });
    }
    return sortableItems;
  }, [items, sortConfig]);

  const requestSort = (key) => {
    let direction = 'ascending';
    if (
      sortConfig &&
      sortConfig.key === key &&
      sortConfig.direction === 'ascending'
    ) {
      direction = 'descending';
    }
    setSortConfig({ key, direction });
  };

  return { items: sortedItems, requestSort, sortConfig };
};

const ProductTable = (props) => {
  const { items, requestSort, sortConfig } = useSortableData(props.products);
  const getClassNamesFor = (name) => {
    if (!sortConfig) {
      return;
    }
    return sortConfig.key === name ? sortConfig.direction : undefined;
  };
  return (
    <table>
      <caption>Products</caption>
      <thead>
        <tr>
          <th>
            <button
              type="button"
              onClick={() => requestSort('name')}
              className={getClassNamesFor('name')}
            >
              Name
            </button>
          </th>
          <th>
            <button
              type="button"
              onClick={() => requestSort('price')}
              className={getClassNamesFor('price')}
            >
              Price
            </button>
          </th>
          <th>
            <button
              type="button"
              onClick={() => requestSort('stock')}
              className={getClassNamesFor('stock')}
            >
              In Stock
            </button>
          </th>
        </tr>
      </thead>
      <tbody>
        {items.map((item) => (
          <tr key={item.id}>
            <td>{item.name}</td>
            <td>${item.price}</td>
            <td>{item.stock}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

function App() {
  return (
    <div className="App">
      <ProductTable
        products={[
          { id: 1, name: 'Cheese', price: 4.9, stock: 20 },
          { id: 2, name: 'milk', price: 1.9, stock: 32 },
          { id: 3, name: 'Yoghurt', price: 2.4, stock: 12 },
          { id: 4, name: 'Heavy Cream', price: 3.9, stock: 9 },
          { id: 5, name: 'butter', price: 0.9, stock: 99 },
          { id: 6, name: 'Sour Cream ', price: 2.9, stock: 86 },
          { id: 7, name: 'Fancy French Cheese 🇫🇷', price: 99, stock: 12 },
        ]}
      />
    </div>
  );
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
body {
  font-family: 'open sans', sans-serif;
  font-size: 16px;
}

table {
  width: 100%;
  border-collapse: collapse;
}

thead th {
  text-align: left;
  border-bottom: 2px solid black;
}

thead button {
  border: 0;
  border-radius: none;
  font-family: inherit;
  font-weight: 700;
  font-size: inherit;
  padding: 0.5em;
  margin-bottom: 1px;
}

thead button.ascending::after {
  content: '👇';
  display: inline-block;
  margin-left: 1em;
}

thead button.descending::after {
  content: '☝️';
  display: inline-block;
  margin-left: 1em;
}

tbody td {
  padding: 0.5em;
  border-bottom: 1px solid #ccc;
}

tbody tr:hover {
  background-color: #eee;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

我尝试将abuseSortable改为小写,但由于有带数字的价格列,因此出错。
不管区分大小写,我可以做些什么才能使它工作。

ifsvaxew

ifsvaxew1#

我认为对不同的类型有不同的分类功能是合理的。我建议为字符串类型和数字类型设置一个排序函数,并根据列类型使用它。这样一来,代码就变得更清晰、更易维护了。

r8xiu3jd

r8xiu3jd2#

使用a[sortConfig.key] < b[sortConfig.key]比较2个字符串将导致基于字符代码点而不是字符内容进行比较。根据内容比较两个字符串的一种简单方法是使用localeCompare()
如果您确信a[sortConfig.key]b[sortConfig.key]将始终返回字符串,则可以return a[sortConfig.key].localeCompare(b[sortConfig.key])而无需任何额外检查。
但是,在您的场景中,它们也可以表示pricestock,它们都是数字,因此没有方法localeCompare()。因此,我们应该事先检查这两个值是否都是字符串。
我个人不喜欢输入检查,因为JavaScript是一种简单的类型语言。我通常会检查我需要的方法是否可用。
假设定义了以下帮助器:

function hasMethod(item, methodName) {
  // intentional != usage, checks for both null and undefined
  return item != null && typeof item[methodName] === "function";
}

您可以按以下方式对收藏进行排序:

const modifier = sortConfig.direction === 'ascending' ? 1 : -1;

sortableItems.sort((a, b) => {
  const [valueA, valueB] = [a, b].map(item => item[sortConfig.key]);
  const areLocaleComparable = [valueA, valueB].every(value => hasMethod(value, "localeCompare"));

  if (areLocaleComparable) return valueA.localeCompare(valueB) * modifier;

  return (-(valueA < valueB) || +(valueA > valueB)) * modifier;

  // or instead of the above line
  if (valueA < valueB) return -1 * modifier;
  if (valueA > valueB) return  1 * modifier;
  return 0;
});

或者,如果您更愿意使用类型检查,则可以使用typeof value === "string"而不是hasMethod(value, "localeCompare")

v64noz0r

v64noz0r3#

您可以检查类型,例如:

if (typeof a[sortConfig.key] === "string" && typeof b[sortConfig.key] === "string") {
    if (a[sortConfig.key].toLowerCase() < b[sortConfig.key].toLowerCase()) {
      return sortConfig.direction === 'ascending' ? -1 : 1;
    }
} else {
    if (a[sortConfig.key] < b[sortConfig.key]) {
      return sortConfig.direction === 'ascending' ? -1 : 1;
    }
}

相关问题