Обновить поле в объекте массива состояний - PullRequest
0 голосов
/ 12 октября 2019

Я создаю простое приложение TODO в React. У меня есть массив todos в состоянии, и каждый todo имеет флаг completed. При нажатии на элемент я хочу установить флаг completed для этого todo. Вот что я сделал до сих пор:

Todos.js

class Todos extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: [
          { id:0, title:"Title 0", completed:false },
          { id:1, title:"Title 1", completed:true }
         ]
    };
    this.onTodoClicked = this.onTodoClicked.bind(this);
  }

  onTodoClicked(id) {
    console.log(id + " clicked");
    this.setState(prevState => {
      todos: prevState.todos.map((item, index) => {
        return index !== id ? item : { ...item, completed: !item.completed };
      });
    });
  }

  render() {
    return (
      <div>
        {this.state.todos.map((todoItem, key) => (
          <Todo id={key} key={key} todo={todoItem}
            todoClicked={this.onTodoClicked} />
        ))}
      </div>
    );
  }
}

Todo.js - это простой компонент (да,она также может быть функциональной)

class Todo extends React.Component {
  render() {
    return (
      <div onClick={() => this.props.todoClicked(this.props.id)}>
        {this.props.todo.title}
      </div>
    );
  }
}

В Todos.js вызывается функция onTodoClicked, которая выводит правильный идентификатор, но в состоянии больше ничего не меняется (Iпроверял выбранный объект как с помощью инструментов React dev, так и console.log. Выбранный элемент не меняет свой флаг completed. Что я делаю не так?

Ответы [ 4 ]

2 голосов
/ 12 октября 2019

Было бы лучше положиться на id вместо index:

return item.id !== id ? item : { ...item, completed: !item.completed };

Также для Todo id вы должны передать идентификатор, а не ключ (id={key}) (или только через весь todoItem):

id={todoItem.id}

Для ключа Todo также передайте id, поскольку, передавая тот же индекс key, ваш элемент Todo не может быть повторно отображен.

И, наконец, вам не хватает скобок после prevState => ({


class Todo extends React.Component {
  render() {
    return (
      <div onClick={() => this.props.todoClicked(this.props.todo.id)}>
        {this.props.todo.title} - {this.props.todo.completed.toString()}
      </div>
    );
  }
}

class Todos extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: [
          { id:0, title:"Title 0", completed:false },
          { id:1, title:"Title 1", completed:true }
         ]
    };
    this.onTodoClicked = this.onTodoClicked.bind(this);
  }

  onTodoClicked(id) {
    console.log(id);
    this.setState(prevState => ({
      todos: prevState.todos.map((item, index) => {
        return item.id !== id ? item : { ...item, completed: !item.completed };
      })
    }));
  }

  render() {
    return (
      <div>
        {this.state.todos.map((todoItem, key) => (
          <Todo key={todoItem.id} todo={todoItem}
            todoClicked={this.onTodoClicked} />
        ))}
      </div>
    );
  }
}

ReactDOM.render(<Todos />, document.querySelector("#container"))
<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>
<div id="container"></div>
1 голос
/ 12 октября 2019

Вот как это должно выглядеть:

class Todos extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: [
          { id:0, title:"Title 0", completed:false },
          { id:1, title:"Title 1", completed:true }
         ]
    };
    this.onTodoClicked = this.onTodoClicked.bind(this);
  }

  onTodoClicked(id) {
    console.log(id + " clicked");
    this.setState(prevState => ({
      todos: prevState.todos.map((item, index) => {
        return item.id !== id ? item : { ...item, completed: !item.completed };
      })
    }));
  }

  render() {
    return (
      <div>
        {this.state.todos.map((todoItem, key) => (
          <Todo key={key} todo={todoItem}
            todoClicked={this.onTodoClicked} />
        ))}
      </div>
    );
  }
}

class Todo extends React.Component {
  render() {
    return (
      <div onClick={() => this.props.todoClicked(this.props.todo.id)}>
        {this.props.todo.title} {this.props.todo.completed && "completed"}
      </div>
    );
  }
}

ReactDOM.render(<Todos/>, document.getElementById("root"))
<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>
<div id="root"></div>
0 голосов
/ 12 октября 2019

Это то, что вы можете сделать в Todo.js

class Todo extends React.Component {
  render() {
    return (
     <div onClick={() => this.props.todoClicked(this.props.todo.id)}>
       {this.props.todo.title}
     </div>
  );
 }
}

А в функции onTodoClicked вы можете сделать что-то вроде этого.

onTodoClicked(id) {
    this.setState(prevState => {
      todos: prevState.todos.map((item) => {
       return item.id !== id ? item : { ...item, completed: !item.completed };
     });
   });
  }

Надеюсь, это поможет вам.

0 голосов
/ 12 октября 2019

Вы должны внести изменения в onTodoClicked Function.it будет работать.

onTodoClicked(id) {
        console.log(id + " clicked");
        this.state.todos[id].completed = !this.state.todos[id].completed;
        this.setState({ todos: this.state.todos });
        console.log("state,", this.state.todos);
      }

Это ссылка: https://stackblitz.com/edit/react-yxxksp?file=index.js

...