Функция отображения массива не возвращает обновленные элементы

#javascript #reactjs #array-map

#javascript #reactjs #array-map

Вопрос:

Я сталкиваюсь со странной проблемой (по крайней мере, для меня), когда метод массива JS map() не возвращает обновленный массив в соответствии с логом обратного вызова карты. У меня есть следующая функция

 1  handleChange (id){
2     this.setState((prevState)=>{
3      const todos = prevState.myTodos.map(item => {
4        if(item.id === id){
5          //console.log("ITEM.id is ",item.id,"id passed is ",id,"status is ",item.completed)
6          item.completed = !item.completed
7        }
8        //console.log("ITEM.id is ",item.id,"id passed was ",id,"new status is ",item.completed)
9        return item
10      })
11      return {myTodos : todos}
12    })
13  }
 

В этой функции я обновляю состояние объекта приложения.
Непосредственно перед возвращаемым элементом в строке 8 я могу записать вывод и увидеть, что item.completed изменен, но когда обратный вызов возвращает объект item, он не сохраняет обновленный элемент в todos переменной.

Любая подсказка может быть очень полезной. Спасибо в ожидании

Ниже приведен полный код: App.js Компонент

 import React from 'react';
//Importing components
import Todo from './components/Todo';

import './App.css';
//import todosData from './todosData';
const todosData=[  {
      id:1,
      text:"Take out the trash",
      completed:true,
      imageUrl : "https://environmentamerica.org/sites/environment/files/w-takeout-trash-wk1003mike-shutterstock_422191876.jpg"
  },
  {
      id:2,
      text:"Grocery Shopping",
      completed:false,
      imageUrl : "https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Supermarkt.jpg/240px-Supermarkt.jpg"

  },
  {
      id:3,
      text:"Clean gecko tank",
      completed:false,
      imageUrl : "https://i.ytimg.com/vi/hy7eLGcCU9Q/hqdefault.jpg"
  },
  {
      id:4,
      text:"Mow Lawn",
      completed:true,
      imageUrl : "https://cdn.theatlantic.com/thumbor/hbIVuzfmIWAZZ_zKMj1CKSeMkAM=/507x56:2754x1320/720x405/media/img/mt/2016/07/RTR20MER/original.jpg"
  }
];

class App extends React.Component {
  constructor(){
    super()
    this.state={
      myTodos : todosData,
      counter : 1
    }
    this.handleClick = this.handleClick.bind(this)
    this.handleChange = this.handleChange.bind(this)
  }
  handleClick (){
    this.setState((prevState)=>{

      const counterUpdated = prevState.counter  1
      return {counter : counterUpdated}
      
    })
  }
  handleChange (id){
     this.setState((prevState,anything)=>{
      const todos = prevState.myTodos.map(item => {
        if(item.id === id){
          console.log("ITEM.id is ",item.id,"id passed is ",id,"status is ",item.completed)
          item.completed = !item.completed
        }
        console.log("ITEM.id is ",item.id,"id passed was ",id,"new status is ",item.completed)
        return item
      })
      return {myTodos : todos}
    })
  }
  render(){
    const todoItems = this.state.myTodos.map(item=> {return <Todo key={item.id} todo={item} handleChange={this.handleChange} />})
    return (
    <div className="App">
      <header className="App-header">
        React Course from vSchool. <button onClick={this.handleClick} >Counter {this.state.counter}</button>
      </header>
      <main className="todo-container">
        <div className="todo-list">
        {todoItems}
        </div>
      </main>
      <footer className="footer">This is copy right of no one except for images :)</footer>
      
      </div>

  );}
}
export default App;
 

Компонент Todo :

 import React, { useState } from 'react'

function Todo(props){
    const [width,setWidth]=useState(140);
    const [height,setHeight]=useState(85);
    return(
        <div className="todo-item">
            <p style={{display : 'none'}}>{props.todo.id}</p>
            <p>{props.todo.text}</p>
            <input type="checkbox" checked={props.todo.completed}
             
             onChange={()=>{props.handleChange(props.todo.id)}}
             
             />
            <img 
            // onMouseOver={(e)=>{setWidth(250);setHeight(250);}} 
            // onMouseOut={(e)=>{setWidth(140);setHeight(85);}}
            src={props.todo.imageUrl} width={width} height={height}/>
            <hr />
        </div>
    );
}
export default Todo;
 

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

1. Если бы вы могли опубликовать свой полный код на этой странице, это могло бы помочь вам лучше достичь желаемых результатов

2. @AppCity обновил код.

3. вы не возвращали объект в истинном состоянии, поэтому он всегда будет возвращать старый, поэтому он не менялся, я добавил ответ, пожалуйста, проверьте

Ответ №1:

Вы могли бы попытаться сделать ваше условие if встроенным, чтобы вы его не использовали return . Что-то вроде:

 handleChange (id){
    this.setState((prevState)=>{
    const todos = prevState.myTodos.map(item => 
            (item.id === id) ? {...item, completed: !item.completed} : item )
    })
}
 

Отредактировано: это решение работает, потому что оно возвращает новый объект JS с использованием синтаксиса spread {...item, completed: !item.completed} . В свою очередь, это вызывает новый вызов рендеринга.

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

1. в предоставленном вами коме кода перед элементом в предпоследней строке : указано, что код работает и выдает желаемый результат. Я принимаю ответ, поскольку он решает проблему. Спасибо за вашу помощь. но не могли бы вы немного объяснить, почему возврат может не сработать?

2. Что на самом деле исправило это { ...item, completed: !item.completed } , так это то, что создается новый объект JS, который запускает react для повторного выполнения задачи. Поскольку ваш код модифицировал существующий объект, это не вызвало бы повторной отправки

Ответ №2:

Пожалуйста, проверьте этот фрагмент

 import React, { Component } from "react";
import "./styles.css";

class App extends Component {
  state = {
    myTodos: [
      { id: 1, title: "wake up", completed: false },
      { id: 2, title: "brush", completed: false }
    ]
  };

  handleChange = (id) => {
    this.setState((prevState) => ({
      myTodos: prevState.myTodos.map((todo) => {
        if (todo.id === id) {
          return { ...todo, completed: !todo.completed };
        }
        return todo;
      })
    }));
  };

  render() {
    return (
      <div className="App">
        <h1>TODOS</h1>
        {this.state.myTodos.map((todo) =>
          todo.completed ? (
            <del>
              <p onClick={() => this.handleChange(todo.id)}>{todo.title}</p>
            </del>
          ) : (
            <p onClick={() => this.handleChange(todo.id)}>{todo.title}</p>
          )
        )}
      </div>
    );
  }
}

export default App;
 

Рабочий codesandbox

Проблема:

  1. Вы не возвращали объект в истинном состоянии