【问题标题】:Sort fetched data对获取的数据进行排序
【发布时间】:2022-01-28 02:36:32
【问题描述】:

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

我从 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 fetch-api


    【解决方案1】:

    您使用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

    【讨论】:

    • 非常感谢!我必须了解更多关于 useMemo 的信息,因为我以前没有使用过它
    • 不客气!当然,有多种方法可以实现这一点。 Arslan 的回答也可以。
    • 这绝对是一个很好的、中肯的、现代的解释。
    【解决方案2】:

    这可能是因为 set state 函数本质上是异步的,并且 setData 的调用顺序与您预期的不同。 因此,对于使用 sortType 'default' 的初始调用,您在返回数据时不会注意到任何变化。但是,一旦将其更改为“降序”,setData() 来自 sortData() 的调用将比来自 fetchData() 的调用更早,因此您的状态中已经有数据,您会在一段时间内看到 UI 中的数据发生变化,但随后来自函数 fetchDatasetData() 被调用,并将您的数据替换为您从 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.
    

    【讨论】:

      猜你喜欢
      • 2021-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-29
      • 2010-11-07
      • 2019-04-09
      相关资源
      最近更新 更多