[英]React Hook useEffect has a missing dependency for the setter of useState in React
[英]React useState hook with dependency
为什么useState()
没有dependency
数组,例如:
const [state, setState] = useState<T>(initialState, [initialState]);
在 React 中,我经常遇到这种情况
export function EditComponent<T>(props: {
initialState: T,
onSave: (value: T) => void,
}) {
const { initialState, onSave } = props;
const [state, setState] = useState<T>(initialState);
useEffect(() => setState(initialState), [initialState]);
function revert() {
setState(initialState);
}
function save() {
onSave(state);
}
return (
...
)
}
我有一个提供一些数据的外部组件和一个允许用户编辑它(通过修改数据的副本)然后最终保存或恢复的内部组件。 当然,我需要内部组件对任何外部变化做出反应(也许新数据已经从网络或其他地方获取)。
我不喜欢这样做:
const [state, setState] = useState<T>(initialState);
useEffect(() => setState(initialState), [initialState]);
根据我对 React 的理解,这是在initialState
更改时渲染组件两次:
initialState
更改)。 在这个循环中, useEffect
更新被添加到渲染后执行的queue
中。useEffect
更新后的第二种类型。 我需要的是useState
上的dependency
数组,以执行以下操作:
const [state, setState] = useState<T>(initialState, [initialState]);
看起来很简单,就像initialState
在第一个渲染周期中被同步消费并变得可用一样,当依赖列表发生变化时,应再次执行此操作。
我试图自己实现它,但我想出的似乎更像是一个 hack:
import { DependencyList, Dispatch, SetStateAction, useRef, useState } from 'react';
interface MemoContext<S> {
deps: DependencyList | undefined;
state?: S
}
// Is dependency list equal (L327 areHookInputsEqual)
function areHookInputsEqual(a: DependencyList | undefined, b: DependencyList | undefined): boolean {
if (!a) {
console.error('Prev deps should not be null')
return false;
} else if (!b) {
return false;
}
for (let i = 0; i < a.length && i < b.length; i++) {
if (!Object.is(a[i], b[i])) {
return false;
}
}
return true;
}
export function useMemoState<S>(
initialState: S | (() => S),
deps?: DependencyList,
): [S, Dispatch<SetStateAction<S>>] {
function resetInitialState() {
const s: S = typeof initialState === 'function' ? (initialState as any)() : initialState;
ctx.state = s;
ctx.deps = deps;
return s;
}
const ctx = useRef<MemoContext<S>>({ deps: undefined, state: undefined }).current;
// this is actually used just to preserve the rendering behaviour
const [state, setState] = useState<S>(resetInitialState);
if (!areHookInputsEqual(ctx.deps, deps)) {
// They are different, perform the update
resetInitialState()
}
function dispatch(action: SetStateAction<S>) {
setState(prevState => {
const s: S = typeof action === 'function' ? (action as any)(prevState) : action;
ctx.state = s;
ctx.deps = deps;
return s;
})
}
return [ctx.state!, dispatch];
}
老实说, React Core似乎是不应该碰的东西。 所以我想知道我是否遗漏了一些东西,以及是否有明确的原因说明不存在这样的功能。 或者也许有更好的解决方案?
一个简短的答案 - 我们不需要它=)
这是您重构的代码片段:
export function EditComponent<T>(props: {
initialState: T,
onSave: (value: T) => void,
}) {
const { initialState, onSave } = props;
const [state, setState] = useState<T>(initialState);
if (initialState !== state){
setState(initialState);
}
function revert() {
setState(initialState);
}
function save() {
onSave(state);
}
return (
...
)
}
只需在渲染期间有条件地更新 state 即可。
有 2 个链接可能对您有所帮助:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.