我正在开发一个小待办事项应用程序,作为使用React的练习。我有一个这样的模拟服务:
export default class TodoService {
constructor(todos) {
this.todos = new Map();
todos.forEach(todo => {
this.todos.set(todo.id, todo);
});
}
findAll() {
return Array.from(this.todos.values());
}
saveTodo(todo) {
this.todos[todo.id] = todo
}
completeTodo(todo) {
this.todos.delete(todo.id)
}
}
在我的React应用程序中,我有一些包含TODO的状态:
const [todos, setTodos] = useState([]);
const [flip, flipper] = useState(true);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
flipper(!flip);
}
useEffect(() => {
setTodos(todoService.findAll());
}, [flip])
completeTodo
是一个函数,我将它传递到我的Todo
组件中,当我想完成这样一个Todo时使用:
import React from "react";
const Todo = ({ todo, completeFn }) => {
return (
<form className="todo">
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
value=""
name={todo.id}
id={todo.id}
onClick={() => {
console.log(`completing todo...`)
completeFn(todo)
}} />
<label className="form-check-label" htmlFor={todo.id}>
{todo.description}
</label>
</div>
</form>
)
}
export default Todo
因此,每当用户单击复选框completeFn
并使用todo
调用时,就会从服务对象中删除它,并且状态应该更新,但最奇怪的事情发生了。
当调用TodoService.completeTodo()
时,todo将被正确删除,但当调用findAll()
时,旧的todo仍然存在!如果我将内容写入控制台,我可以看到该项目被删除,然后在调用findAll
时以某种方式远程传送回来。为什么会发生这种情况?我这么说是因为我不懂什么魔法?
编辑:更疯狂的是,如果我将其修改为仅在初始加载时使用效果,如下所示:
const [todos, setTodos] = useState([]);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
setTodos(todoService.findAll());
}
useEffect(() => {
setTodos(todoService.findAll());
}, [])
我得到了一个非常奇怪的结果:
有人能给我解释一下吗?
Edit2:这是一个完整的可复制示例(没有index.html
和
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Todo = ({ todo, completeFn }) => {
return (
<div>
<input
type="checkbox"
name={todo.id}
id={todo.id}
onClick={() => {
console.log(`completing todo...`)
completeFn(todo)
}} />
<label className="form-check-label" htmlFor={todo.id}>
{todo.description}
</label>
</div>
)
}
class TodoService {
constructor(todos) {
this.todos = new Map();
todos.forEach(todo => {
this.todos.set(todo.id, todo);
});
}
findAll() {
return Array.from(this.todos.values());
}
saveTodo(todo) {
this.todos[todo.id] = todo
}
completeTodo(todo) {
this.todos.delete(todo.id)
}
}
const App = () => {
let todoService = new TodoService([{
id: 1,
description: "Let's go home."
}, {
id: 2,
description: "Take down the trash"
}, {
id: 3,
description: "Play games"
}]);
const [todos, setTodos] = useState([]);
const [flip, flipper] = useState(true);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
flipper(!flip);
}
useEffect(() => {
setTodos(todoService.findAll());
}, [flip])
return (
<div>
{todos.map(todo => <Todo key={todo.id} todo={todo} completeFn={completeTodo} />)}
</div>
)
};
ReactDOM.render(<App />, document.getElementById("root"));
在此场景中,您不需要调用useffect
。您已经在useffect
中放置了一个依赖项,可以使用它来停止无限循环。但这里没有必要。您实际上没有执行任何提取操作
您可以将代码更新为这样。
import React, { useState, useCallback, useEffect } from "react";
const Todo = ({ todo, completeFn }) => {
const handleOnclick = useCallback(() => {
// useCallback since function is passed down from parent
console.log(`completing todo...`);
completeFn(todo);
}, [completeFn, todo]);
return (
<div>
<input
type="checkbox"
name={todo.id}
id={todo.id}
onClick={handleOnclick}
/>
<label className="form-check-label" htmlFor={todo.id}>
{todo.description}
</label>
</div>
);
};
class TodoService {
constructor(todos) {
this.todos = new Map();
todos.forEach(todo => {
this.todos.set(todo.id, todo);
});
}
findAll() {
console.log(Array.from(this.todos.values()));
return Array.from(this.todos.values());
}
saveTodo(todo) {
this.todos[todo.id] = todo;
}
completeTodo(todo) {
this.todos.delete(todo.id);
}
}
const todoService = new TodoService([
{
id: 1,
description: "Let's go home."
},
{
id: 2,
description: "Take down the trash"
},
{
id: 3,
description: "Play games"
}
]);
export default function App() {
const [todos, setTodos] = useState([]); // Set initial state
const completeTodo = todo => {
todoService.completeTodo(todo);
setTodos(todoService.findAll()); // Update state
};
useEffect(() => {
setTodos(todoService.findAll());
}, []); // Get and update from service on first render only
return (
<div>
{todos.map(todo => (
<Todo key={todo.id} todo={todo} completeFn={completeTodo} />
))}
</div>
);
}
工作示例
https://codesandbox.io/s/cranky-hertz-sewc5?file=/src/App.js
招呼: 我认为我没有正确更新我的复杂状态: 如何正确更新此状态?谢谢 https://codesandbox.io/s/intelligent-ellis-qi97k?file=/src/App.js
我有3个div。当我点击特定的按钮时,每个人都会有类活动。它们的每个div都有一个状态变量,每当它是真的时,我都会向连接到它的div添加class Name='active': 当我点击按钮时,状态改变,但是div没有激活类 当我将函数更改为此时,它会工作: ================================================ =====================
问题内容: 抱歉,我真的很想念React中子组件内部的传递。 我已经实现了一个包含3个组件的待办事项列表。 有一个组成部分和一个组成部分。状态仅存储在组件中。 显示器起步不错,因此可以看到道具。但是在提交表单后,我收到了以下错误: TypeError:this.props.tasks.map不是函数 当我console.log时,我没有得到我的数组,而是数组的长度。 你知道为什么吗? 编辑 :谢谢
我正在尝试使用useReducer更新组件的状态。我在useEffect中从mongodb获取了一些信息,在那里我调用了我的useReducer dispatch,以便在组件装载后立即设置我的状态。不幸的是,我的状态没有受到影响,我的UI也没有改变。控制台。useffect语句末尾的log语句显示我的状态仍然具有useReducer调用中设置的初始值。有人能告诉我哪里出了问题吗?另外,我计划稍后在
问题内容: 这可能看起来很奇怪。 我已经用Java(在Eclipse中)编写了代码。然后,我对代码进行了一些修改。现在,我正在尝试运行新代码(已修改),但是它仍然为我提供了先前代码的输出。 我在代码中放置了很少的调试点,但是它跳过了一些调试点(尽管它应该在它们处停止)并在某个调试点处停止,但是即使在这里,它也调用了先前代码中存在的方法。位置(尽管我已经对此发表了评论)。从某个地方看来,它仍然在调试
问题内容: 我正在尝试新的React Hooks,并有一个带有计数器的Clock组件,该计数器应该每秒增加一次。但是,该值不会增加到超过一。 问题答案: 原因是传递给的闭包中的回调仅访问第一个渲染器中的变量,而无法访问后续渲染器中的新值,因为第二次未调用。 回调中的值始终为0 。 就像您熟悉的状态挂钩一样,状态挂钩有两种形式:一种是处于更新状态的状态,另一种是将当前状态传入的回调形式。您应该使用第