对获取的数据进行排序

问题描述 投票:0回答:2

我需要对获取的数据添加排序(升序/降序)。

我从 API 端点获取所有数据。我映射该数组中的每个对象以显示在单独的组件卡中。但是,一旦我选择按降序名称对数据进行排序,我就会快速更改组件,它们从 Z 排序到 A,但它会立即转换回初始获取的状态(从 A 到 Z)。

您能告诉我问题出在哪里吗?我不知道为什么,但感觉排序数组没有保存在我用来映射所有卡片的状态“数据”中。

import { useState } from 'react';
import { useEffect } from 'react';
import './styles/main.scss';
import Card from './components/Card/Card';
import { v4 as uuidv4 } from 'uuid';

function App() {
  const [data, setData] = useState([]);
  const [sortType, setSortType] = useState('default');

  useEffect(() => {
    fetchData();
    sortData();
  }, [sortType]);

  const fetchData = async () => {
    const response = await fetch(
      'https://restcountries.com/v2/all?fields=name,region,area'
    );
    const data = await response.json();
    setData(data);
  };

  function sortData() {
    let sortedData;
    if (sortType === 'descending') {
      sortedData = [...data].sort((a, b) => {
        return b.name.localeCompare(a.name);
      });
    } else if (sortType === 'ascending') {
      sortedData = [...data].sort((a, b) => {
        return a.name.localeCompare(b.name);
      });
    } else {
      return data;
    }
    setData(sortedData);
  }

  return (
    <div className='content'>
      <header className='content__header'>
        <h1>Header placeholder</h1>
      </header>
      <div className='wrapper'>
        <div className='wrapper__sort-buttons'>
          <select
            defaultValue='default'
            onChange={(e) => setSortType(e.target.value)}
          >
            <option disabled value='default'>
              Sort by
            </option>
            <option value='ascending'>Ascending</option>
            <option value='descending'>Descending</option>
          </select>
        </div>
        <ul className='wrapper__list'>
          {data.map((country) => {
            country.key = uuidv4();
            return (
              <li key={country.key}>
                <Card
                  name={country.name}
                  region={country.region}
                  area={country.area}
                />
              </li>
            );
          })}
        </ul>
      </div>
    </div>
  );
}

export default App;

这就是我短暂得到的:

然后它就回到初始状态:

reactjs sorting fetch-api
2个回答
2
投票

您使用

useEffect
的方式似乎会导致您的组件在每次更改排序类型时重新获取数据。由于多个位置在不同时间更新数据状态,这可能会导致竞争条件。

我会将排序逻辑移至

useMemo
中,并仅在初始加载时获取
useEffect
中的数据:

import { useEffect, useMemo, useState } from "react";
import './styles/main.scss';
import Card from './components/Card/Card';
import { v4 as uuidv4 } from "uuid";

function App() {
  const [data, setData] = useState([]);
  const [sortType, setSortType] = useState("default");

  // Move sort logic here...
  const sortedData = useMemo(() => {
    let result = data;

    if (sortType === "descending") {
      result = [...data].sort((a, b) => {
        return b.name.localeCompare(a.name);
      });
    } else if (sortType === "ascending") {
      result = [...data].sort((a, b) => {
        return a.name.localeCompare(b.name);
      });
    }

    return result;
  }, [data, sortType]);

  // Only fetch data once on component mount...
  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    const response = await fetch(
      "https://restcountries.com/v2/all?fields=name,region,area"
    );
    const data = await response.json();
    setData(data);
  };

  return (
    <div className="content">
      <header className="content__header">
        <h1>Header placeholder</h1>
      </header>
      <div className="wrapper">
        <div className="wrapper__sort-buttons">
          <select
            defaultValue="default"
            onChange={(e) => setSortType(e.target.value)}
          >
            <option disabled value="default">
              Sort by
            </option>
            <option value="ascending">Ascending</option>
            <option value="descending">Descending</option>
          </select>
        </div>
        <ul className="wrapper__list">
          {/* Use sortedData here instead of data... */}
          {sortedData.map((country) => {
            country.key = uuidv4();
            return (
              <li key={country.key}>
                <Card
                  name={country.name}
                  region={country.region}
                  area={country.area}
                />
              </li>
            );
          })}
        </ul>
      </div>
    </div>
  );
}

export default App;

这是 Codesandbox 中的一个基本示例(我注释掉了您的样式/卡片组件):https://codesandbox.io/s/goofy-tdd-8lio9?file=/src/App.js


1
投票

发生这种情况的原因可能是 set state 函数本质上是异步的,并且调用 setData 的顺序与您预期的不同。 因此,对于使用 sortType 'default' 的初始调用,当您按原样返回数据时,您不会注意到任何更改。但是,一旦将其更改为“降序”,来自

setData()
sortData() 
会比来自
fetchData()
的调用更早,因此,由于您的状态中已经有数据,您会在 UI 中看到数据的变化一段时间,但随后函数
setData()
中的
fetchData
被调用,并将您的数据替换为从 API 调用中获得的未排序或升序的数据。

可能的解决方案

不要在

fetchData
方法中设置状态,而只需在
sortData
方法中设置一次,因为无论如何你都需要它。 所以你的代码看起来像这样:

  // we will call sortData inside fetchData so remove it from here
  useEffect(() => {
    fetchData();
  }, [sortType]);

  const fetchData = async () => {
    const response = await fetch(
      'https://restcountries.com/v2/all?fields=name,region,area'
    );
    const data = await response.json();
    // using API response data as an input to sortData function
    sortData(data)
  };

  // using data from parameter instead of state
  function sortData(data) {
    let sortedData;
    if (sortType === 'descending') {
      sortedData = [...data].sort((a, b) => {
        return b.name.localeCompare(a.name);
      });
    } else if (sortType === 'ascending') {
      sortedData = [...data].sort((a, b) => {
        return a.name.localeCompare(b.name);
      });
    } else {
      return data;
    }
    setData(sortedData);
  }

改进

您的API调用不依赖于SORTING ORDER,因此您不需要一次又一次调用API,只需调用一次,然后根据下拉列表中更改的值对数据进行排序。

  // call the API on initial load only
  useEffect(() => {
    fetchData();
  }, []);

  // and on sortType change you can handle it like this:
  useEffect(() => {
    sortData(data);
  }, [sortType]);

  // and using this approach you can use the exact same code for both functions implementation that you posted in your question above.
© www.soinside.com 2019 - 2024. All rights reserved.