reactjs 如何使用useState更改数组中对象内部的元素?[副本]

ghhkc1vu  于 2023-08-04  发布在  React
关注(0)|答案(5)|浏览(123)

此问题在此处已有答案

How do I update states onChange in an array of object in React Hooks(13个答案)
10小时前关闭。
我需要根据对象的“todoId”在“todo”元素中进行更改。我该怎么做?例如,如果我只想更改具有“todoId”= 1的对象的“todo”数组。

const [todoList, setTodoList] = useState([
    {
      todoId: 0,
      todoName: "Personal",
      todo: []
    },
    {
      todoId: 1,
      todoName: "Work",
      todo: []
    },
    {
      todoId: 2,
      todoName: "College",
      todo: []
    },
  ])

字符串

axzmvihb

axzmvihb1#

您可以创建一个函数来接收todoId和todo数据。并且如果找到了todo,则更新todo数组。

const update = (id, data) => {
  setTodoList((prevTodos) => {
    const index = prevTodos.findIndex((todo) => todo.todoId === id);

    if (index !== -1) {
      prevTodos[index].todo = prevTodos[index].todo.concat(data);
    }

    return prevTodos;
  });

  console.log(todoList);
};

字符串

z5btuh9x

z5btuh9x2#

下面是一个示例,说明如何从各种待办事项列表中添加和删除项目。
在设置状态时,需要使用函数回调版本。只需克隆列表,找到您想要添加(或删除)的列表,然后返回修改后的克隆。当点击一个按钮时,DOM会被查询数据属性,以识别您正在修改的列表或项目。

**注意:**在下面的例子中,我只是在添加新项目时设置了时间戳。您可以对此进行修改,以显示一个对话框,您可以在其中输入todo项的文本。

另外,避免使用0作为ID。在大多数语言中,zero可能意味着:false、unknown、null、nil、unset或undefined。最好从1开始。

const { useCallback, useEffect, useState } = React;

const fetchTodos = () => Promise.resolve([
  { todoId: 1, todoName: "Personal", todo: [] },
  { todoId: 2, todoName: "Work",     todo: [] },
  { todoId: 3, todoName: "College",  todo: [] },
]);

const TodoItem = ({ text, handleRemove }) => (
  <div className="TodoItem">
    <span>{text}</span>
    <button type="button" onClick={handleRemove}>Remove</button>
  </div>
);

const TodoList = ({ uid, name, items, handleAdd, handleRemove }) => (
  <div className="TodoList">
    <div className="heading">
      <h2>{name}</h2>
      <button type="button" data-uid={uid} onClick={handleAdd}>Add</button>
    </div>
    <ul>
      {items.map(({ text }, index) => (
        <li key={text} data-index={index}>
          <TodoItem text={text} handleRemove={handleRemove} />
        </li>
      ))}
    </ul>
  </div>
);

const App = () => {
  const [todoLists, setTodoLists] = useState([]);
  
  useEffect(() => {
    fetchTodos().then(setTodoLists);
  }, []);
  
  const handleAdd = useCallback((e) => {
    const { uid } = e.target.dataset;
    setTodoLists((currentTodoLists) => {
      const copy = structuredClone(currentTodoLists);
      const found = copy.find(({ todoId }) => todoId === +uid);
      if (found) {
        found.todo.push({ text: Date.now() });
        return copy;
      }
      return currentTodoLists;
    });
  }, []);
  
  const handleRemove = useCallback((e) => {
    const { index } = e.target.closest('li').dataset;
    const { uid } = e.target.closest('.TodoList').querySelector('.heading button').dataset;
    setTodoLists((currentTodoLists) => {
      const copy = structuredClone(currentTodoLists);
      const found = copy.find(({ todoId }) => todoId === +uid);
      if (found) {
        found.todo.splice(+index, 1);
        return copy;
      }
      return currentTodoLists;
    });
    
  }, []);

  return (
    <div className="App">
      <h1>Todos</h1>
      {todoLists.map(({ todoId, todoName, todo }) => (
        <TodoList
          key={todoId}
          uid={todoId}
          name={todoName}
          items={todo}
          handleAdd={handleAdd}
          handleRemove={handleRemove}
        />
      ))}
    </div>
  );
};

ReactDOM
  .createRoot(document.getElementById("root"))
  .render(<App />);
h1, h2 { margin: 0; padding: 0; margin-bottom: 0.25rem; }

.TodoList ul {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;;
}

.TodoList .heading,
.TodoItem {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
nbysray5

nbysray53#

下面是一个基于您的数据的扩展示例。关键部分在handleClick中。在从下拉列表中选择了该部分后,在输入框中输入了todo,并单击了按钮...
...我们更新setTodoList的状态,方法是取前一个状态并Map到它上面。如果区段名称等于select状态的值,我们spread将现有的区段对象转换为一个新对象(无变异!)。我们将部分的todos数组扩展到一个新数组中,添加一个带有id的新todo(基于todo数组的当前长度)。最后返回更新后的section对象。如果section名称和select值不匹配,我们返回section对象,不做任何更改。
(Note,对于本例,我已经更改了数据中的属性键名称,使其更有意义。)

const { useState } = React;

function Example({ initial }) {

  // States for the list, the select, and the input
  const [ todoList, setTodoList ] = useState(initial);
  const [ select, setSelect ] = useState('Choose section');
  const [ input, setInput ] = useState('');

  // Update the input state
  function handleInput(e) {
    setInput(e.target.value);
  }

  // When the button is clicked
  function handleClick() {
  
    // `map` over the previous state
    setTodoList(prev => {
      return prev.map(section => {
      
        // If the select value equals the current iterated
        // section name
        if (section.name === select) {

          // Spread the existing section in a new object
          // then spread out the existing section todos array
          // into a new array adding in a new todo object with its
          // own id
          return {
            ...section,
            todos: [
              ...section.todos,
              { id: section.todos.length + 1, text: input }
            ]
          };
        }
        
        // Otherwise just return the section
        return section;
      });
    });
  }

  // Update the select
  function handleSelect(e) {
    if (e.target.value !== 'Choose section') {
      setSelect(e.target.value);
    }
  }

  // Disabled the button if either the select or input values
  // are "empty"
  function isButtonDisabled() {
    return select === 'Choose section' || input === '';
  }

  return (
    <main>
      <section>
        <select value={select} onChange={handleSelect}>
          <option disabled>Choose section</option>
          {todoList.map(section => {
            return <Option key={section.id} option={section} />
          })}
        </select>
        <input type="text" onChange={handleInput} />
        <button
          type="button"
          disabled={isButtonDisabled()}
          onClick={handleClick}
        >Add todo
        </button>
      </section>
      <section>
        {todoList.map(section => {
          return (
            <TodoSection
              key={section.id}
              name={section.name}
              todos={section.todos}
            />
          );
        })}
      </section>
    </main>
  );

}

function Option({ option }) {
  return (
    <option
      value={option.name}
    >{option.name}
    </option>
  );
}

function TodoSection({ name, todos }) {
  return (
    <section className="section">
      <header>{name}</header>
      <ul>
        {todos.map(todo => {
          return <Todo key={todo.id} todo={todo} />
        })}
      </ul>
    </section>
  );
}

function Todo({ todo }) {
  return (
    <li>
      {todo.text}
    </li>
  );
}

const todoList=[{id:0,name:"Personal",todos:[]},{id:1,name:"Work",todos:[]},{id:2,name:"College",todos:[]}];

const node = document.getElementById('root');
const root = ReactDOM.createRoot(node);
root.render(<Example initial={todoList} />);
main > section ~ section { margin-top: 1rem; }
.section header { font-weight: 600; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
<div id="root"></div>
k5ifujac

k5ifujac4#

您需要基本上重新创建整个数组。

setTodoList((prev) => {
let updateObject= prev.filter((item) => item.todoId===1)
// do things to updateObject
return ([...prev.filter((item) => item.todoId!==1), updateObject])
})

字符串

r6vfmomb

r6vfmomb5#

useState钩子返回的updater函数(在本例中为setTodoList)可以与以下任一项一起使用:

  • 一个值(例如,setTodoList([])将其设置为空数组)
  • 返回值的回调。

回调接受一个prevState参数,它只是前一个状态值。然后回调应该返回一个新的状态值。
这对于数组状态变量非常有效,因为你通常会想从数组中添加/更改/删除一个项目。
这里有一些你可以尝试:

function changeItem (itemId, newItem) {
    setTodoList(function (oldTodoList) {
        // create a new array without the item where item.todoId == itemId
        const itemsWithoutNewId = oldTodoList.filter((item) => item.id !== id);
        // add the new item and this array together and put it in another array
        const itemsWithNew = [...itemsWithoutNewId, newItem];
        // Return the final array
        return itemsWithNew;
    });
}

字符串
或作为一行程序(功能等同):

const changeItem = (id, newItem) => setTodoList(oldTodoList => [...oldTodoList.filter(item => item.todoId !== id), newItemForId]);


它们都可以这样使用:

const item = {
      todoId: 1,
      todoName: "New Todo",
      todo: []
};
changeItem(1, item);

console.log(todoList);
/*
[
    {
      todoId: 0,
      todoName: "Personal",
      todo: []
    },
    {
      todoId: 1,
      todoName: "New Todo",
      todo: []
    },
    {
      todoId: 2,
      todoName: "College",
      todo: []
    },
]
*/

相关问题