#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
Проблема:
- Вы не возвращали объект в истинном состоянии