Флажок не меняет своего значения при нажатии в приложении React todo

#javascript #arrays #reactjs

#javascript #массивы #reactjs

Вопрос:

Пожалуйста, помогите мне с этим, я не совсем понимаю, где именно я делаю неправильно. Поэтому, когда я нажимаю на флажок, значения не меняются (если это по умолчанию true , когда я нажимаю на щелчок, он должен false быть). Для этого onChange в todoList компоненте in я вызываю handleClick функцию, там я меняю todo.completed значение (в основном переключая значения).

App.js Внутри handleClick метода, когда do console.log(todo) перед возвратом из функции map значение переключается нормально, но оно не обновляется в updatedTodo .

App.js

 import TodosData from "./todoData";
import TodoList from "./todoList";
import "./styles.css";

class App extends Component {
  constructor() {
    super();
    this.state = {
      todos: TodosData
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(id) {
    this.setState(prevState => {
      const updatedTodo = prevState.todos.map(todo => {
        // console.log(updatedTodo);
        if(todo.id === id) {
          console.log("before the opt " todo.completed);
          todo.completed = !todo.completed
          console.log("after the opt " todo.completed);
        }
        //console.log(todo);
        return todo;
      })
      console.log(updatedTodo);
      return {
        todos: updatedTodo
      }
    });
  }

  render() {

    const todoDataComponents = this.state.todos.map(item => {
      return <TodoList key = {item.id} item = {item} handleChange = {this.handleChange} />
    })
    return (
      <div className="todo-list">{todoDataComponents}</div>
    );
  }
}

export default App;
 

Список задач.jsx

 

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        // console.log(this.props)
        return (
            <div className="todo-item">
                <input
                    type="checkbox"
                    checked={this.props.item.completed}
                    onChange = {() => this.props.handleChange(this.props.item.id)}
                />
                <p>{this.props.item.text}</p>
            </div>
        );
    }
}

export default TodoList;
 

todoData.js

 const TodosData = [
    { id: 1, text: "Coding", completed: true },
    { id: 2, text: "Exercise", completed: false },
    { id: 3, text: "Learning", completed: true },
    { id: 4, text: "Programming", completed: true },
    { id: 5, text: "inspire", completed: false },
    { id: 6, text: "motivation", completed: true }
];

export default TodosData;
 

Ответ №1:

Я не вижу никаких ошибок в вашем handleChange() методе, он должен работать нормально. Однако я только что обновил часть вашего кода, который вы можете протестировать ниже.

Я изменил ваше название TodoList , так как на самом деле это не список, а элемент. Я также изменил его на функциональный компонент, поскольку он является только презентационным, нет необходимости иметь собственное состояние. Вместо того чтобы добавлять p тег после input , вы должны использовать a label , чтобы сделать его доступным.

Я на самом деле ничего не изменил в вашем handleChange() методе, только удалил console.log s, и он работает так, как ожидалось.

Обновление: вы используете React.StrictMode , где React отображает все дважды в dev. Поскольку ваш handleChange() запуск completed выполняется дважды, он дважды устанавливает состояние нажатой задачи, возвращая ее в исходное состояние. Поэтому, если он false включен при первом рендеринге, он устанавливается на true on click, но он снова отображается, а на втором возвращается false . Вы не заметите этого, так как это происходит довольно быстро.

Чтобы избежать этого, вам нужно избегать каких-либо изменений. Итак, я обновил ваш onChange обработчик, он возвращает новый объект, если completed свойство изменено.

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

 const TodosData = [
  { id: 1, text: 'Coding', completed: true },
  { id: 2, text: 'Exercise', completed: false },
  { id: 3, text: 'Learning', completed: true },
  { id: 4, text: 'Programming', completed: true },
  { id: 5, text: 'Inspire', completed: false },
  { id: 6, text: 'Motivation', completed: true },
];

function TodoItem(props) {
  const { handleChange, item } = props;

  return (
    <div className="todo-item">
      <input
        type="checkbox"
        id={`item-${item.id}`}
        checked={item.completed}
        onChange={() => handleChange(item.id)}
      />
      <label htmlFor={`item-${item.id}`}>{item.text}</label>
    </div>
  );
}

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: TodosData,
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(id) {
    this.setState((prevState) => {
      const updatedTodo = prevState.todos.map((todo) => {
        return todo.id === id ? {
          ...todo,
          completed: !todo.completed,
        } : todo;
      });

      return {
        todos: updatedTodo,
      };
    });
  }

  render() {
    const { todos } = this.state;

    return (
      <div className="todo-list">
        {todos.map((item) => (
          <TodoItem
            key={item.id}
            item={item}
            handleChange={this.handleChange}
          />
        ))}
      </div>
    );
  }
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>, 
  document.getElementById('root')
); 
 body {
  font-family: sans-serif;
  line-height: 1.6;
} 
 <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> 

Ответ №2:

Вместо того чтобы использовать дополнительную переменную, вы можете сделать это в одной строке. Можете ли вы посмотреть, работает ли это для вас.

 handleChange(id) {
    this.setState(prevState => prevState.map(todo => todo.id === id ? {...todo, completed: !todo.completed} : todo )
}
 

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

1. Привет, @Кто-то, у меня это не работает. Я попытался с помощью `handleChange (id) { this.setState (prevState => prevState.todos.map(todo => todo.id === идентификатор? {…todo, завершено: !todo.завершено}: todo )) }` вы можете проверить мой код в [link] ( codesandbox.io/s/recttodo-cmbqz?file=/src/App.js:314-617 )