reactjs - 在 react hooks 中生成新的组件类型,如何保持性能?
问题描述
我正在探索一个生成包装器组件的钩子,以减少向我的 react-native 应用程序添加动画的样板。我已经写了一个返回动画值并在内部管理它们的钩子,它工作得很好。然而,每次我使用它时,我都必须编写应用动画值的样板,如下所示:
//pseudocode
const [animatedValue, animationRef, ...otherUsefuLStuff] = useMyGenericAnimationHook(...some animation config)
return (
<Animated.View style={{transform: [{translateY: animatedValue}]}}>
.....
</Animated.View>
)
这没什么大不了的,但它仍然是我必须为每个动画添加的一些样板。我正在考虑是否可以从我的动画钩子中返回一个包装器组件来抽象出这个样板,如下所示
//pseudocode
const [AnimatedTranslateY, animatedValue, animationRef, ...otherUsefulStuff] = useMyGenericAnimationHook(... some animation config)
return (
<AnimatedTranslateY>.....</AnimatedTranslateY>
)
这看起来更简洁,但是从钩子返回一个新的组件类型肯定会导致问题。React 将在每次渲染时拆除并重建子组件,因为每次 useMyAnimationHook() 运行时都会返回一个新的组件类型!
我最初的想法是在我的钩子中记住返回的组件类型
//pseudocode
const useMyGenericAnimationHook= (....configuration arguments) => {
const animatedValueRef = ....create the animated value(s) ref(s)
const WrapperView: React.FC<ViewProps> = useMemo(
() =>
React.memo(({ children, style, ...restProps }) => (
<Animated.View {...restProps}
style={[style, { transform: ...apply animated value(s)}]}>
{children}
</Animated.View>
)),
[animatedValueRef.current]
)
return [WrapperView, animationHandle, otherUsefulStuff]
}
这就是我有些困惑的地方。我认为这应该可以正常工作,而不是在每次渲染时重建树。组件类型应该保持稳定,除非给 useMemo 的依赖项在我们希望它渲染的时候发生变化。然而,我对此并不完全有信心。
使用时反应的行为是什么<WrapperView>
?
这不是一个好的模式有什么理由吗?
感谢您的任何见解!
解决方案
这里还有一个想法。我将稍微调整一下你的钩子 API,这样你就不会返回新组件(只是向Animated.View
组件添加道具),而是返回一组返回包装器道具的函数。然后,您调用所需的函数并将结果合并到组件的样式属性中。例子:
function useMyGenericAnimationHook(...animationConfig) {
let animatedValue, animationRef, otherUsefulStuff;
return {
animatedValue,
animationRef,
...otherUsefulStuff,
// here's the meat of it
translateY: () => ({
transform: [{translateY: animatedValue}]
}),
color: () => ({
color: `hsl(120,100%,${animatedValue}%)`
}),
};
}
function Component() {
let { translateY, color } = useMyGenericAnimationHook(...animationConfig);
return (
<Animated.View style={{ ...translateY(), ...color() }}>
{children}
</Animated.View>
);
}
旧答案
我喜欢这种模式——它看起来很干净。只要您确保它不会在每次渲染时生成新的组件类型,它就应该是高性能的,并且useMemo
应该为此工作。
另一种选择是将组件移到挂钩之外。如果这适用于您的用例,则它保证渲染之间的引用相等。但是,它不允许您将道具绑定到组件,因此用户必须提供任何必需的道具。
//pseudocode
const WrapperView = React.memo(({ children, style, ...restProps }: ViewProps) => (
<Animated.View {...restProps}
style={[style, { transform: ...apply animated value(s)}]}>
{children}
</Animated.View>
);
const useMyGenericAnimationHook= (....configuration arguments) => {
const animatedValueRef = ....create the animated value(s) ref(s)
return [WrapperView, animationHandle, otherUsefulStuff]
}
第三种但更笨拙的选择是从钩子中返回 JSX。
function useDiv() {
return <div />;
}
function Component(props) {
const div = useDiv();
return (
<main>{div}</main> // equivalent to <main><div/></main>
)
}
您基本上可以将 JSX 复制粘贴到呈现的输出中,这可能适合您的用例。不过,如果前两个选项都不适合您,我只会使用它。
推荐阅读
- reactjs - 使用 Antd 组件的 Next.js 页面不加载
- git - 如何获取 LFS 文件的实际大小?
- javascript - 添加到对象时不会出现额外的键/值对
- java - 在 Linux 中,java.io.Console 的实例是否代表当前 JVM 进程的控制终端?
- specman - 在 Specman 中同时更改多个结构中的布尔字段
- spring - Spring Data MongoDB + Spring Boot 启动两次?
- php - 为 foreach() Laravel 5.5 提供的参数无效
- php - 使用会话变量比多次读取 ini 文件更有效
- django - Django Race Condition 当在 form_valid 中增加使用表时
- java - 如何从 OpenStreetMaps API 的给定地址获取坐标