gpt4 book ai didi

javascript - setState 如何与 memo 一起使用 react

转载 作者:行者123 更新时间:2023-12-03 18:58:18 27 4
gpt4 key购买 nike

我正在学习 React,试图制作一个任务管理器应用程序。我正在尝试用备忘录记住组件,但是这样做时状态表现奇怪,当通过单击 2 个不同任务的复选框完成任务时,其他 gif 附件未完成,以便您理解我的意思:
Image from Gyazo
我留下一个包含完整代码的代码框:
Edit react-app-manger
和 git 存储库:https://github.com/FrancoRodao/learning-react/tree/master/src
任务组件:

import React, { useState, useEffect } from 'react'
import TaskRow from "../TaskRow";

const initialState = [{"id":1,"title":"1","description":"","done":false},{"id":2,"title":"2","description":"","done":false},{"id":3,"title":"3","description":"","done":true}]

function Tasks(props) {

const [taskItems, setTaskItems] = useState(initialState)

useEffect(() => {
if (!props.newTask) return
newTask({ id: taskItems.length + 1, ...props.newTask })
}, [props.newTask])


const newTask = (task) => {
updateItems([...taskItems, task].map((task) => ({ ...task })))
}

const toggleDoneTask = (id) => {
let taskItemsCopy = [...taskItems].map((task) => ({ ...task }))
let newItems = taskItemsCopy.map((t) => {
if (t.id === id) {
t.done = !t.done
};
return t;
})
updateItems(newItems)
}

const updateItems = (tasks) => {
localStorage.setItem('tasks', JSON.stringify(tasks))
setTaskItems(tasks)
}

return (
<React.Fragment>
<h1>learning react </h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Done</th>
</tr>
</thead>
<tbody>
{

props.show ? taskItems.map((task, i) =>
<TaskRow
task={task}
key={task.id}
toggleDoneTask={toggleDoneTask}>
</TaskRow>)
:

taskItems.filter((task) => !task.done)
.map((task) =>
<TaskRow
show={props.show}
task={task}
key={task.id}
toggleDoneTask={toggleDoneTask}></TaskRow>
)
}
</tbody>
</table>
</React.Fragment>
)
}


export default Tasks
Task Row(任务项)组件
import React, { memo } from 'react'
import styled from "styled-components"

const Tr = styled.tr`
display: block;
/* height: ${props => props.show && props.taskDone ? '100%' : '0px'};
opacity: ${props => props.show && props.taskDone ? '1' : '0'};
visibility: ${props => props.show && props.taskDone ? 'visible' : 'hidden'};
transition: 0.2s; */
`;

function TaskRow(props) {

return (<React.Fragment>
{console.log('render', props.task)}
<Tr show={props.show} taskDone={props.task.done}>
<td>
{props.task.title}
</td>
<td>
{props.task.description}
</td>
<td>
<input type="checkbox"
checked={props.task.done}
onChange={() => props.toggleDoneTask(props.task.id)}
/>

</td>
</Tr>
</React.Fragment>)

}


export default memo(TaskRow, (prev, next) => {
const prevTaskKeys = Object.keys(prev.task);
const nextTaskKeys = Object.keys(next.task);

const sameLength = prevTaskKeys.length === nextTaskKeys.length;
const sameEntries = prevTaskKeys.every(key => {
return nextTaskKeys.includes(key) && prev.task[key] === next.task[key];
});

return sameLength && sameEntries;
})
我在另一个问题中找到了一个解决方案,将 useReduce 和 setTaskItems 作为函数,但我不明白如果我不多次修改状态或类似的东西,为什么我必须将它用作函数。

最佳答案

问题是当toggleDoneTask被内存。当它被内存时,它的内部引用状态值也被内存,对应于一个过时的状态值。
单击 1 后,任务 2 和 3 将使用相同的先前 Prop ,其中 toggleDoneTask没有当前状态。
要解决这个问题,您需要重构 toggleDoneTask .而不是将状态更新为 setTaskItems(tasks)您需要将函数传递给您的 setState,例如 setTaskItems(prevTasks => { // ... some logic; return nextask}) .传递函数,prevTasks参数将永远是正确的。
鉴于这一切,如果你重构 toggleDoneTask如下所示,一切都会按预期工作:

const toggleDoneTask = (id) => {
setTaskItems(taskItems => {
let taskItemsCopy = [...taskItems].map((task) => ({ ...task }))
let newItems = taskItemsCopy.map((t) => {
if (t.id === id) {
t.done = !t.done
};
return t;
})
localStorage.setItem('tasks', JSON.stringify(newItems))
return newItems
})
}

关于javascript - setState 如何与 memo 一起使用 react,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65365879/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com