首页 > 解决方案 > React 不会更新 DOM 中的排序项

问题描述

所以当我想在 React 中对数组中的元素进行排序时遇到了问题。

问题不在于排序逻辑,而在于 React 本身,所以每当我想单击一个显示“按难度排序”的按钮时,它都不起作用。但是,当我在同一时刻单击“隐藏”按钮时,DOM 中的元素会完全按照我的意愿进行更新。

是什么导致了这个问题以及如何解决它?谢谢

编辑:代码沙盒链接 https://codesandbox.io/s/tender-rubin-1nq0r

import React, { useState, useEffect } from 'react';
// import { ListProvider } from '../context/listContext.js';
import useAjax from '../custom-hooks/useAjax.js';
import TodoForm from './form.js';

import './todo.scss';

const ToDo = () => {
  const [_addItem, _toggleComplete, _getTodoItems, _deleteItems, _hideItems, sorted, list] = useAjax()
  useEffect(_getTodoItems, []);

  const [currentPage, setCurrentPage] = useState(1)
  const [todosPerPage, setTodosPerPage] = useState(3)
  const pageNumbers = []
  const indexOfLastTodo = currentPage * todosPerPage;
  const indexOfFirstTodo = indexOfLastTodo - todosPerPage;

  let currentTodos = list.slice(indexOfFirstTodo, indexOfLastTodo)
  for (let i = 1; i <= Math.ceil(list.length / todosPerPage); i++) {
    pageNumbers.push(i);
  }


  return (
    <>
      <header>
        <h2>
          There are {list.filter(item => !item.complete).length} Items To Complete
        </h2>
      </header>

      <section className="todo">

        <div>
          <TodoForm handleSubmit={_addItem} />
        </div>

        <div>
          <ul>
            {currentTodos.map(item => (
              <li
                className={`complete-${item.complete.toString()}`}
                key={item._id}
              >
                <span onClick={() => _toggleComplete(item._id)}>
                  {item.text}
                </span>
                <small>{item.difficulty}</small>
                <button onClick={() => _deleteItems(item)}>X</button>
              </li>
            ))}
            {
              pageNumbers.map(number => {
                return (
                  <button
                    key={number}
                    id={number}
                    onClick={(event) => { setCurrentPage(Number(event.target.id)) }}
                  >
                    {number}
                  </button>
                );
              })
            }
            <button onClick={_hideItems}>hide</button>
            <button onClick={sorted}>Sort By Difficulty</button>
          </ul>
        </div>
      </section>
    </>
  );
};

export default ToDo;



// **************************THe custom Hook file it attached below :******************************************

import { useState } from 'react'
import axios from 'axios'

const todoAPI = 'https://api-js401.herokuapp.com/api/v1/todo'
export default () => {
    const [list, setList] = useState([])
    const _addItem = (item) => {
        item.due = new Date()
        axios.post(todoAPI, JSON.stringify(item), {
            headers: { 'Content-Type': 'application/json' }
        })
            .then(data => {
                setList([...list, data.data])

            })
    }
    const _toggleComplete = id => {
        let item = list.filter(i => i._id === id)[0] || {};
        if (item._id) {
            item.complete = !item.complete;
            let url = `${todoAPI}/${id}`;
            axios.put(url, JSON.stringify(item), {
                headers: { 'Content-Type': 'application/json' }
            })
                .then(data => {
                    setList(list.map((listItem) => listItem._id === item._id ? data.data : listItem))
                })
        }
    }
    const _getTodoItems = async () => {
        let rawData = await axios.get(todoAPI)
        let data = rawData.data.results
        setList(data)
    }
    const _deleteItems = async item => {
        let url = `${todoAPI}/${item._id}`
        let deletedItem = await axios.delete(url)
        setList(list.filter((listItem) => listItem._id === deletedItem.data._id ? '' : listItem))
    }
    const _hideItems = () => {
        setList(list.filter(listItem => (
            listItem.complete === false
        )))
    }
    const sorted = () => {
        setList(list.sort((a, b) => {
            return b.difficulty - a.difficulty
        }))


    }
    return [_addItem, _toggleComplete, _getTodoItems, _deleteItems, _hideItems, sorted, list]
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

标签: reactjs

解决方案


发生这种情况是因为.sort(...)不返回新数组而是返回相同的数组,即它不是新的对象引用。你必须明确地这样做,这样React才能知道我有一个新的对象引用,这意味着我应该导致一个re-render

hide实现中,您使用.filter(...)on 会list为您返回一个新数组,因此您不必显式执行此操作。

useAjax只需在钩子中更改您的排序实现,如下所示:-

    const sorted = () => {
        setList([...list.sort((a, b) => {
            return b.difficulty - a.difficulty
        })])
    }

...是用于创建列表的浅表副本的扩展运算符。

分叉沙箱 - https://codesandbox.io/s/epic-leakey-vr925?file=/src/custom-hooks/useAjax.js


推荐阅读