React:使用回调还是在内部组件中定义?
React: to useCallback or define in inner component?
我有两个组成部分,它们是:
const ChildOn = (/*...*/) => {
//...
}
const Parent = () => {
const [is42, setIs42] = useState(true)
return (is42 ? <ChildOff ... > : <ChildOn ... />)
}
ChildOff
的定义不重要。
我想将它们定义为以下任何一种,但我无法决定是哪种:
根据父项中的 variable/function 在每个子项中声明子项中使用的函数。
type ChildOnProp = { setIs42: Dispatch<SetStateAction<boolean>> };
const ChildOn = ({ setIs42 }: ChildOnProp) => {
const f1 = () => { setIs42(true); };
return <Text onPress={f1} />;
};
const Parent = () => {
return is42
? <ChildOff setIs42={setIs42} />
: <ChildOn setIs42={setIs42} />;
};
在父级内部定义子级使用的函数。
type ChildOnProps = { func: () => void }
const ChildOn = ({ func }: ChildOnProps) => {
return <Text onPress={func} />
}
const Parent = () => {
const [is42, setIs42] = useState(true)
const f1 = useCallback(() => { setIs42(true) })
const f2 = useCallback(() => { setIs42(false) })
return (is42 ? <ChildOff func={f2} /> : <ChildOn func={f1} />)
}
虽然 (1) 对我来说更漂亮,但 (2) 似乎更有效率。然而我不知道我是否适合这样判断,因为我读过很多关于 React 的文章,在什么时候最好使用 useCallback
.
上相互矛盾
React 社区警告不要进行无用的记忆,因为在某些情况下它可能会增加复杂性而没有任何好处。
在这种情况下,我认为值得记住回调,因为它会减少子组件不必要的渲染次数,否则可能会产生负面影响,甚至会随着时间的推移引入问题。
为此,有一种创建自定义挂钩的常见模式,通常称为 useToggle
,它会记住常见的设置器。
import {useState, useMemo} from 'react';
export function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
// Defined once, so guaranteed stability
const setters = useMemo(() => ({
toggle: () => setValue(v => !v),
setFalse: () => setValue(false),
setTrue: () => setValue(true),
setValue,
}), [setValue]);
// Defined each time the value changes, so less than every render.
return useMemo(() => ({
...setters,
value
}), [value, setters]);
}
这可以在父级中使用:
const Parent = () => {
const { value: is42, setTrue, setFalse } = useToggle(true);
return (is42 ? <ChildOff func={setFalse}> : <ChildOn func={setTrue} />);
}
TypeScript 会自动推断所有类型,因此它开箱即用。
如果有多个状态值需要切换回调,我们可以通过不解构来轻松识别每个状态值。
const firstState = useToggle(true);
const secondState = useToggle(true);
//...
return (
<>
<Foo onClick={firstState.toggle} />
<Bar onClick={secondState.toggle} />
</>
);
作为参考,这里有一个simpler implementation from Shopify。
至于记忆过多或过少,Meta (Facebook) 显然 experimenting with memoizing everything by default 在转译时,因此开发人员不必考虑它。
我有两个组成部分,它们是:
const ChildOn = (/*...*/) => {
//...
}
const Parent = () => {
const [is42, setIs42] = useState(true)
return (is42 ? <ChildOff ... > : <ChildOn ... />)
}
ChildOff
的定义不重要。
我想将它们定义为以下任何一种,但我无法决定是哪种:
根据父项中的 variable/function 在每个子项中声明子项中使用的函数。
type ChildOnProp = { setIs42: Dispatch<SetStateAction<boolean>> }; const ChildOn = ({ setIs42 }: ChildOnProp) => { const f1 = () => { setIs42(true); }; return <Text onPress={f1} />; }; const Parent = () => { return is42 ? <ChildOff setIs42={setIs42} /> : <ChildOn setIs42={setIs42} />; };
在父级内部定义子级使用的函数。
type ChildOnProps = { func: () => void } const ChildOn = ({ func }: ChildOnProps) => { return <Text onPress={func} /> } const Parent = () => { const [is42, setIs42] = useState(true) const f1 = useCallback(() => { setIs42(true) }) const f2 = useCallback(() => { setIs42(false) }) return (is42 ? <ChildOff func={f2} /> : <ChildOn func={f1} />) }
虽然 (1) 对我来说更漂亮,但 (2) 似乎更有效率。然而我不知道我是否适合这样判断,因为我读过很多关于 React 的文章,在什么时候最好使用 useCallback
.
React 社区警告不要进行无用的记忆,因为在某些情况下它可能会增加复杂性而没有任何好处。
在这种情况下,我认为值得记住回调,因为它会减少子组件不必要的渲染次数,否则可能会产生负面影响,甚至会随着时间的推移引入问题。
为此,有一种创建自定义挂钩的常见模式,通常称为 useToggle
,它会记住常见的设置器。
import {useState, useMemo} from 'react';
export function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
// Defined once, so guaranteed stability
const setters = useMemo(() => ({
toggle: () => setValue(v => !v),
setFalse: () => setValue(false),
setTrue: () => setValue(true),
setValue,
}), [setValue]);
// Defined each time the value changes, so less than every render.
return useMemo(() => ({
...setters,
value
}), [value, setters]);
}
这可以在父级中使用:
const Parent = () => {
const { value: is42, setTrue, setFalse } = useToggle(true);
return (is42 ? <ChildOff func={setFalse}> : <ChildOn func={setTrue} />);
}
TypeScript 会自动推断所有类型,因此它开箱即用。
如果有多个状态值需要切换回调,我们可以通过不解构来轻松识别每个状态值。
const firstState = useToggle(true);
const secondState = useToggle(true);
//...
return (
<>
<Foo onClick={firstState.toggle} />
<Bar onClick={secondState.toggle} />
</>
);
作为参考,这里有一个simpler implementation from Shopify。 至于记忆过多或过少,Meta (Facebook) 显然 experimenting with memoizing everything by default 在转译时,因此开发人员不必考虑它。