反应挂钩,useState相关的问题。如何更新对象的一个​​属性并保留其余属性

时间:2020-10-09 17:09:21

标签: javascript reactjs react-hooks

我想做一个带有钩子的待办事项清单。这是我的相关代码。

function generateId() {
  return '_' + Math.random().toString(36).substr(2, 9);
};
export function TodoList(){
  const [todos, setTodos] = React.useState([])
  const [input, setInput] = React.useState('')
  const [time, setTime] = React.useState('')

  const handleSubmit = () => {
    setTodos((todos) => todos.concat({
      text: input,
      id: generateId(),
      timeRequired: time,
    }))
    setInput('')
    setTime('')
  }
  // remove to do works fine.
  const removeTodo = (id) => setTodos((todos) => todos.filter((todo) => todo.id !== id ))

  /// confusion here 
  let todo = (id) => todos.find(x => x.id = id)
  const decrement = (id) => setTodos((todo(id).timeRequired) => timeRequired - 1) 
  /// 
  return(
    <React.Fragment>
      <input type="text" value={input} placeholder="New Task" onChange={(e) => setInput(e.target.value)}> 
      </input>
      <input type="number" value={time} placeholder="Hours Required" onChange={(e) => setTime(e.target.value)}>
      </input>
      <button onClick={handleSubmit}> Submit </button>
      <ul>
        {todos.map(({text, id, timeRequired}) => (
          <li key={id}>
            <span>{text}: remaining {timeRequired} hours </span> 
            <button onClick={() => removeTodo(id)}> 
              Cancel ❌ 
            </button>
            <button onClick={() => decrement(id)}> Decrement ➖ </button> 
            <button > Increase ➕ </button> 
            <button> Done ✔️ </button>
          </li>
        ))}
      </ul>
    </React.Fragment>
  )
}

所以我想增加/减少待办事项列表上剩余的时间。但是,我不知道如何选择列表中的特定项目并更改一个属性(剩余时间)并保留text属性。

谢谢。

4 个答案:

答案 0 :(得分:2)

这是decrement函数的外观。这将占用id,并在所有todos上循环。如果todo与提供的id不匹配,请保持原样。如果匹配,则创建对象的浅表副本({...todo}),并使用更新后的值覆盖timeRequired属性。

const decrement = (id) => setTodos((todos) => todos.map((todo) => {
  if (todo.id != id) return todo;
  return {...todo, timeRequired: todo.timeRequired - 1};
}));

请注意,{...todo, timeRequired: todo.timeRequired - 1}的顺序很重要。通过将timeRequired: todo.timeRequired - 1放在...todo之后,它将覆盖现有属性。就像对象{a: 1, b: 2, a: 3}将使您拥有对象{a: 3, b: 2}一样,因为键a的第二个定义将覆盖第一个。

答案 1 :(得分:1)

一旦您确定了数组中的元素,就从待办事项中使用递减的timeRequired属性创建一个新对象。然后使用.slice在新项目前后对数组进行切片,并将新项目置于中间:

const decrement = (i) => {
    const replacedTodo = {
        ...todos[i],
        timeRequired: todos[i].timeRequired - 1,
    };
    setTodos(
        todos.slice(0, i),
        replacedTodo,
        todos.slice(i + 1),
    );
};
{todos.map(({ text, id, timeRequired }, i) => (
  // ...
  <button onClick={() => decrement(i)}> Decrement ➖ </button>

直接使用.map函数中的索引要比以后从ID中找到匹配的元素容易。

答案 2 :(得分:1)

您可以这样尝试


  const decrement = (id) => {
    
    const targetTodo = todos.find(x => x.id == id);  //get the todo

    const currentTime = targetTodo.timeRequired -1 //decrease the time 
    
    const newTodo = { ...targetTodo , timeRequired : currentTime } // create new todo object
    
    const newTodoList = todos.map( item => { // finally replace the old todo object
        if(item.id = id)  return newTodo
        else return item;
    })
    
    setTodos(newTodoList) 

  }

答案 3 :(得分:1)

尝试一下...

const removeTodo = (id) => setTodos((todos) => todos.filter((todo) => todo.id !== id ))
const decrement = (id) => setTodos((todos) => todos.map((todo) => {
  const {
    id: oldID,
    timeRequired,
  } = todo;
  return {
    ...todo,
    timeRequired: id===oldID? timeRequired-1 : timeRequired, 
  };
}));