You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React表格排序箭头图标不显示问题求助(附代码)

React表格排序图标不显示问题解决

问题描述

我正在构建一个React表格组件,需求是根据列的排序状态(asc/desc)显示对应的向上(IoCaretUp)或向下(IoCaretDown)箭头图标。但目前无论排序状态如何,箭头都无法显示:

  • Name列已添加条件判断:{nameOrder === 'desc' ?? <IoCaretDown />}、{nameOrder === 'asc' ?? <IoCaretUp />},控制台明确显示nameOrder状态为'asc',但该列仍无箭头;
  • Continent和Population列暂未添加条件判断,直接渲染两个图标。

完整组件代码:

import { useEffect, useState } from 'react';
import './App.css';
import { IoCaretDown } from "react-icons/io5";
import { IoCaretUp } from "react-icons/io5";

function Countries() {

  const [nameOrder, setNameOrder] = useState('asc');
  const [contOrder, setContOrder] = useState('asc');
  const [popOrder, setPopOrder] = useState('asc');
  console.log('name', nameOrder);
  
  const sortByName = () => {
    const sorted = [...data].sort((a, b) => {
      if (nameOrder === 'asc') {
        if (b.name.common > a.name.common) {
          return -1;
        }
        if (b.name.common > a.name.common) {
          return 1;
        }
        return 0;
      } else {
        if (b.name.common < a.name.common) {
          return -1;
        }
        if (b.name.common < a.name.common) {
          return 1;
        }
        return 0;
      }
    });
    setData(sorted);
    console.log('nameOrder', nameOrder)
    let newOrder = nameOrder === 'asc' ? 'desc' : 'asc';
    setNameOrder(newOrder);
    console.log('nameOrder', nameOrder)
  };

  const sortByContinent = () => {
    const sorted = [...data].sort((a, b) => {
      if (contOrder === 'asc') {
        if (b.continents > a.continents) {
          return -1;
        }
        if (b.continents > a.continents) {
          return 1;
        }
        return 0;
      } else {
        if (b.continents < a.continents) {
          return -1;
        }
        if (b.continents < a.continents) {
          return 1;
        }
        return 0;
      }

    });
    setData(sorted);
    console.log('current order', contOrder)
    let newOrder = contOrder === 'asc' ? 'desc' : 'asc';
    setContOrder(newOrder);
    console.log('current order', contOrder)
  };

  const sortByPopulation = () => {
    const sorted = [...data].sort((a, b) => {
      if (popOrder === 'asc') {
        return a.population - b.population;
      } else {
        return b.population - a.population;
      }
    });
    setData(sorted);
    console.log('popOrder', popOrder)

    let newOrder = popOrder === 'asc' ? 'desc' : 'asc';
    setPopOrder(newOrder);
    console.log('popOrder', popOrder)
  };

  const [data, setData] = useState([]);
  useEffect(() => {
    fetch('https://restcountries.com/v3.1/all?fields=name,continents,population,flag')
      .then((resp) => resp.json())
      .then((apiData) => {
        setData(apiData);
      }, []);
    console.log('data', data)

  }, []);
  return (
    <div className="app-container">
      <h1 style={{ textAlign: "center" }}>Country Table Data</h1>

      <table>
        <thead>
          <tr>
            <th onClick={sortByName}>
              Name
              {nameOrder === 'desc' ?? <IoCaretDown />}
              {nameOrder === 'asc' ?? <IoCaretUp />}
            </th>
            <th onClick={sortByContinent}>
              Continent
              <IoCaretDown />
              <IoCaretUp />

            </th>
            <th onClick={sortByPopulation}>
              Population
              <IoCaretDown />
              <IoCaretUp />
            </th>
            <th>Flag</th>
          </tr>
        </thead>
        <tbody>
          {data.map((country) => (
            <tr key={country.flag}>
              <td>{country.name.common}</td>
              <td>{country.continents}</td>
              <td>{country.population}</td>
              <td>{country.flag}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default Countries;

核心解决方案:替换错误的运算符

你错误使用了??(空值合并运算符),它仅在左侧值为null或undefined时才返回右侧内容。而nameOrder === 'desc'/nameOrder === 'asc'的结果是布尔值true或false,这两个值都不属于空值范畴,所以图标组件不会被渲染。

把??替换为&&(逻辑与运算符)即可:

<th onClick={sortByName}>
  Name
  {nameOrder === 'desc' && <IoCaretDown />}
  {nameOrder === 'asc' && <IoCaretUp />}
</th>

额外优化建议

  1. 简化排序逻辑
    字符串排序推荐使用localeCompare,避免重复判断,同时兼容大小写:

    const sortByName = () => {
      const sorted = [...data].sort((a, b) => {
        const nameA = a.name.common.toLowerCase();
        const nameB = b.name.common.toLowerCase();
        return nameOrder === 'asc' 
          ? nameA.localeCompare(nameB) 
          : nameB.localeCompare(nameA);
      });
      setData(sorted);
      setNameOrder(prev => prev === 'asc' ? 'desc' : 'asc');
    };
    
  2. 使用函数式更新状态
    当更新状态依赖于之前的状态值时,用函数式更新可以避免闭包导致的旧值问题:

    // 替换原有的setNameOrder(newOrder)
    setNameOrder(prevOrder => prevOrder === 'asc' ? 'desc' : 'asc');
    
  3. 修正useEffect的控制台输出
    setData是异步操作,初始console.log('data', data)只会输出空数组,应该把日志放在请求回调里:

    useEffect(() => {
      fetch('https://restcountries.com/v3.1/all?fields=name,continents,population,flag')
        .then((resp) => resp.json())
        .then((apiData) => {
          setData(apiData);
          console.log('data', apiData);
        });
    }, []);
    
  4. 替换表格行的key值
    country.flag可能存在重复,建议使用唯一标识字段,比如country.name.common:

    <tr key={country.name.common}>
    

内容的提问来源于stack exchange,提问作者D.Hodges

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 14:47:58