Как я могу изменить значение элемента в массив без изменения других в React

#javascript #reactjs

#javascript #reactjs

Вопрос:

У меня есть простая структура todolist для состояния компонента. Это массив с двумя полями, называемыми ‘content’, и одним, называемым ‘done’. Я перехватываю щелчок элемента строки с помощью простой функции onClick (), передаваемой от родительского элемента к дочернему элементу TodoItem (как предлагает руководство по React):

   render() {
   const tthis = this;

   var myList = tthis.state.todos.map(function(todo,index){

      var myOnCLick = function(){
        var newTodo = {content: todo.content, done: !todo.done};
        tthis.state.todos[index] = newTodo;
        tthis.setState({});

      }
      return <Todo key={index} todo={todo} onClick={myOnCLick}/>
   })

   return (
     <ul className="list">
       { myList }
     </ul>
   )
 }
  

Этот код работает без проблем, но мне это не очень нравится.
Я бы нашел какое-нибудь хорошее решение для изменения значения одного элемента в массиве. Я обнаружил, что в помощнике по неизменяемости React DOC:

{$set: any} полностью замените цель.

И в хорошем ответе на этом форуме я видел пример:

 this.setState({
  todos: update(this.state.todos, {1: {done: {$set: true}}})
  

Но я не могу использовать 1 в моем случае. У меня есть индекс, который дает мне индекс нажатой задачи в списке задач.

Ответ №1:

Вы можете изменить значение done свойства для отдельного объекта todo, перечисленного в массиве, следующим образом:

 flipDone(id) {
  let index = Number(id);

  this.setState({
    todos: [
      ...this.state.todos.slice(0, index),
      Object.assign({}, this.state.todos[index], {done: !this.state.todos[index].done}),
      ...this.state.todos.slice(index   1)
    ]
  });
}
  

Вот демонстрация:http://codepen.io/PiotrBerebecki/pen/jrdwzB

Полный код:

 class TodoList extends React.Component {
  constructor() {
    super();
    this.flipDone = this.flipDone.bind(this);
    this.state = {
      todos: [
        {content: 'Go shopping', done: false},
        {content: 'Walk the dog', done: false},
        {content: 'Wash the dishes', done: false},
        {content: 'Learn React', done: false}
      ]
    };
  }

  flipDone(id) {
    let index = Number(id);

    this.setState({
      todos: [
        ...this.state.todos.slice(0, index),
        Object.assign({}, this.state.todos[index], {done: !this.state.todos[index].done}),
        ...this.state.todos.slice(index   1)
      ]
    });
  }

  render() {
    const myList = this.state.todos.map((todo, index) => {
      return (
        <Todo key={index} 
              clickHandler={this.flipDone} 
              content={todo.content}
              done={todo.done}
              id={index}
        />
      );
    })

    return (
      <ul className="list">
        {myList}
      </ul>
    );
  }
}


class Todo extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    this.props.clickHandler(event.target.id);
  }

  render() {
    return (
      <li>
        <button onClick={this.handleClick}
                id={this.props.id}>
          Click me
        </button> --- 
        {String(this.props.done)} --- 
        {this.props.content}      
      </li>
    );
  }
}


ReactDOM.render(
  <TodoList />,
  document.getElementById('app')
);
  

Комментарии:

1. Хотите, чтобы я что-нибудь добавил к вышесказанному или это отвечает на ваш вопрос?

2. Я никогда не использовал синтаксис из трех точек. Ваше решение отлично работает, но все элементы в списке вызывают render каждый раз 🙂

3.Синтаксис из трех пунктов называется оператором распространения. Например: let cold = ['autumn', 'winter']; let warm = ['spring', 'summer']; console.log([...cold, ...warm]) => ['autumn', 'winter', 'spring', 'summer'] Больше информации здесь: developer.mozilla.org/en/docs/Web/JavaScript/Reference /…