如何根据变量值使 useEffect react hook 重新渲染?
How can I make useEffect react hook rerender based on a variable value?
所以我得到了这个组件:
export default function () {
const [todos, setTodos] = useState([]);
useEffect(() => {
function populateTodos () {
axios.get(`http://localhost:8000/api/all-todos`)
.then(res => setTodos(res.data))
.catch(err => console.log(err));
}
populateTodos();
}, []);
console.log(todos);
return (
<div>
...
</div>
);
}
我正在使用 useEffect
挂钩从数据库中获取所有待办事项,它工作正常。问题是我不知道如何在我对 todos 数组进行修改时使用 useEffect
来触发重新渲染,例如添加、删除或更新。如果我为 useEffect
的依赖项数组提供 todos
变量,我会在控制台中得到一个无限循环日志记录。
我如何使用 useEffect
在 todos
数组更新时触发重新渲染?
问题是 useEffect 内部没有逻辑,请参阅下面的代码
const [todos, setTodos] = useState([]);
useEffect(() => {
setTodos([1])
}, [todos])
这也会产生无限循环。但我们总是给予同样的价值。问题是当它更新时,依赖项为真,因此它再次开始执行 useEffect()。您必须想出一些具体的逻辑,例如 length
已更改,或者您可以采用如下所示的新状态
const [todos, setTodos] = useState([]);
const [load, setLoad] = useState(false);
useEffect(() => {
function populateTodos () {
axios.get(`http://localhost:8000/api/all-todos`)
.then(res => setTodos(res.data))
.catch(err => console.log(err));
}
populateTodos();
}, [load])
console.log(todos)
return (
<div>
<button
onClick={() => {
todos.push(1)
setLoad(!load)
}}
>cilck</button>
</div>
);
您可以通过在父组件中获取待办事项来提升状态,然后将结果作为 prop 传递给 useEffect 依赖于它的子组件。必须在父组件中调用任何 CRUD 操作才能更新列表(但您可以通过将这些函数传递给子组件来触发子组件的修改)。
不然的话,这也是Redux的一个不错的应用。您将能够使用 Redux 获取操作初始化组件,以使用在任何组件上完成的所有 CRUD 操作填充存储,同时通过 API 响应修改 reducer 来更新存储。然后你可以使用商店作为 useEffect 依赖来更新你的组件的本地待办状态。
// State
const [todos, setTodos] = useState(null);
// Redux Stores
const todoStore = useSelector(state => state.todoStore);
// Redux Dispatch
const dispatch = useDispatch();
// Lifecycle: initialize the component's todolist
useEffect(() => {
if (!todos) {
// your Redux Action to call the API fetch & modify the store
dispatch(fetchTodos);
}
}, [dispatch, todos]
// Lifecycle: update the todolist based on changes in the store
// (in this case I assume the data is populated into todoStore.items)
useEffect(() => {
if (todoStore.items.length > 0) {
setTodos(todoStore);
}
}, [todoStore.items]
return {
<div>{ todos.map((todo, index) => <li key={'todo_'+index}>{todo}</li>) }</div>
}
所以我得到了这个组件:
export default function () {
const [todos, setTodos] = useState([]);
useEffect(() => {
function populateTodos () {
axios.get(`http://localhost:8000/api/all-todos`)
.then(res => setTodos(res.data))
.catch(err => console.log(err));
}
populateTodos();
}, []);
console.log(todos);
return (
<div>
...
</div>
);
}
我正在使用 useEffect
挂钩从数据库中获取所有待办事项,它工作正常。问题是我不知道如何在我对 todos 数组进行修改时使用 useEffect
来触发重新渲染,例如添加、删除或更新。如果我为 useEffect
的依赖项数组提供 todos
变量,我会在控制台中得到一个无限循环日志记录。
我如何使用 useEffect
在 todos
数组更新时触发重新渲染?
问题是 useEffect 内部没有逻辑,请参阅下面的代码
const [todos, setTodos] = useState([]);
useEffect(() => {
setTodos([1])
}, [todos])
这也会产生无限循环。但我们总是给予同样的价值。问题是当它更新时,依赖项为真,因此它再次开始执行 useEffect()。您必须想出一些具体的逻辑,例如 length
已更改,或者您可以采用如下所示的新状态
const [todos, setTodos] = useState([]);
const [load, setLoad] = useState(false);
useEffect(() => {
function populateTodos () {
axios.get(`http://localhost:8000/api/all-todos`)
.then(res => setTodos(res.data))
.catch(err => console.log(err));
}
populateTodos();
}, [load])
console.log(todos)
return (
<div>
<button
onClick={() => {
todos.push(1)
setLoad(!load)
}}
>cilck</button>
</div>
);
您可以通过在父组件中获取待办事项来提升状态,然后将结果作为 prop 传递给 useEffect 依赖于它的子组件。必须在父组件中调用任何 CRUD 操作才能更新列表(但您可以通过将这些函数传递给子组件来触发子组件的修改)。
不然的话,这也是Redux的一个不错的应用。您将能够使用 Redux 获取操作初始化组件,以使用在任何组件上完成的所有 CRUD 操作填充存储,同时通过 API 响应修改 reducer 来更新存储。然后你可以使用商店作为 useEffect 依赖来更新你的组件的本地待办状态。
// State
const [todos, setTodos] = useState(null);
// Redux Stores
const todoStore = useSelector(state => state.todoStore);
// Redux Dispatch
const dispatch = useDispatch();
// Lifecycle: initialize the component's todolist
useEffect(() => {
if (!todos) {
// your Redux Action to call the API fetch & modify the store
dispatch(fetchTodos);
}
}, [dispatch, todos]
// Lifecycle: update the todolist based on changes in the store
// (in this case I assume the data is populated into todoStore.items)
useEffect(() => {
if (todoStore.items.length > 0) {
setTodos(todoStore);
}
}, [todoStore.items]
return {
<div>{ todos.map((todo, index) => <li key={'todo_'+index}>{todo}</li>) }</div>
}