Skip to content

更新State中的数组

在没有 mutation 的前提下更新数组 

在 JavaScript 中,数组只是另一种对象。同对象一样你需要将 React state 中的数组视为只读的。这意味着你不应该使用类似于 arr[0] = 'bird' 这样的方式来重新分配数组中的元素,也不应该使用会直接修改原始数组的方法,例如 push()pop()

相反,每次要更新一个数组时,你需要把一个的数组传入 state 的 setting 方法中。为此,你可以通过使用像 filter()map() 这样不会直接修改原始值的方法,从原始数组生成一个新的数组。然后你就可以将 state 设置为这个新生成的数组。

下面是常见数组操作的参考表。当你操作 React state 中的数组时,你需要避免使用左列的方法,而首选右列的方法:

避免使用 (会改变原始数组)推荐使用 (会返回一个新数组)
添加元素pushunshiftconcat[...arr] 展开语法(例子
删除元素popshiftsplicefilterslice例子
替换元素splicearr[i] = ... 赋值map例子
排序reversesort先将数组复制一份(例子

或者,你可以使用 Immer ,这样你便可以使用表格中的所有方法了。

数组的常见操作

更新对象数组中对象的属性

这里要使用map()与展开运算符来实现:

js
import { useState } from 'react';

const initialProducts = [{
  id: 0,
  name: 'Baklava',
  count: 1,
}, {
  id: 1,
  name: 'Cheese',
  count: 5,
}, {
  id: 2,
  name: 'Spaghetti',
  count: 2,
}];

export default function ShoppingCart() {
  const [
    products,
    setProducts
  ] = useState(initialProducts)

  function handleIncreaseClick(productId) {
    setProducts(products.map(product => {
      if (product.id === productId) {
        return {
          ...product,
          count: product.count + 1
        };
      } else {
        return product;
      }
    }))
  }

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}
          {' '}
          (<b>{product.count}</b>)
          <button onClick={() => {
            handleIncreaseClick(product.id);
          }}>
            +
          </button>
        </li>
      ))}
    </ul>
  );
}

向数组中插入元素

使用扩展运算符与slice()

js
  function handleClick() {
    const insertAt = 1; // 可能是任何索引
    const nextArtists = [
      // 插入点之前的元素:
      ...artists.slice(0, insertAt),
      // 新的元素:
      { id: nextId++, name: name },
      // 插入点之后的元素:
      ...artists.slice(insertAt)
    ];
    setArtists(nextArtists);
    setName('');
  }

替换数组中的元素

基本的思想和修改对象数组一样‘

js
function handleIncrementClick(index) {
    const nextCounters = counters.map((c, i) => {
      if (i === index) {
        // 递增被点击的计数器数值
        return c + 1;
      } else {
        // 其余部分不发生变化
        return c;
      }
    });
    setCounters(nextCounters);
  }

添加与删除数组中的内容

js
setArtists( // 替换 state  
	[ // 是通过传入一个新数组实现的  
		...artists, // 新数组包含原数组的所有元素  
		{ id: nextId++, name: name } // 并在末尾添加了一个新的元素  
	]  
);

删除使用filter()实现

使用Immer实现

js
import { useState } from 'react';
import { useImmer } from 'use-immer';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';

let nextId = 3;
const initialTodos = [
  { id: 0, title: 'Buy milk', done: true },
  { id: 1, title: 'Eat tacos', done: false },
  { id: 2, title: 'Brew tea', done: false },
];

export default function TaskApp() {
  const [todos, setTodos] = useImmer(
    initialTodos
  );

  function handleAddTodo(title) {
    setTodos(draft=>{
      draft.push({
        id: nextId++,
        title: title,
        done: false
      });
    }
      
    )
  }

  function handleChangeTodo(nextTodo) {
    // const todo = todos.find(t =>
    //   t.id === nextTodo.id
    // );
    // todo.title = nextTodo.title;
    // todo.done = nextTodo.done;

    setTodos(draft=>{
      const test=draft.find(t=>t.id===nextTodo.id)
      test.title=nextTodo.title
      test.done=nextTodo.done
      
    })
  }

  function handleDeleteTodo(todoId) {
    // const index = todos.findIndex(t =>
    //   t.id === todoId
    // );
    // todos.splice(index, 1);

    setTodos(draft=>{
      const index = draft.findIndex(t =>
       t.id === todoId
     );

      draft.splice(index,1)
    })
  }
// ...