reactjs - React 不会更新 DOM 中的排序项

标签 reactjs

所以当我想在 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>

最佳答案

发生这种情况是因为 .sort(...) 不返回新数组,而是返回相同的数组,即它不是新的对象引用。您必须明确执行此操作,以便 React 知道我有一个新对象引用,这意味着我应该重新渲染

hide 实现中,您在 list 上使用 .filter(...) ,它会为您返回一个新数组,因此你不必明确地这样做。

只需在 useAjax Hook 中更改排序实现,如下所示:-

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

... 是扩展运算符,用于创建列表的浅拷贝

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

关于reactjs - React 不会更新 DOM 中的排序项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66422287/

相关文章:

javascript - 用于 Firefox 的带有 unicode 符号的复选框

reactjs - 使用 'fields' 钩子(Hook)时,有没有办法通过 React DevTools 在 React 多重状态下查看 'useState' 的名称

reactjs - 在 SSR 上 react 水合作用

reactjs - 无法在 React 的 componentWillReceiveProps 中设置状态

javascript - react : How to filter array onClick then setState the unselected buttons

javascript - 如何将 mailerlite.com 注册表单与 React 集成?

javascript - 如何在 create-react-app 中激活 CORS?

reactjs - 对于可能是也可能不是分层的泛型 T 使用什么类型声明?

reactjs - getFieldDecorator 密码重置规则?

reactjs - Apollo 客户端延迟重新获取