如何向FontItem子组件传递基于useSharedValue的selected布尔属性?
如何在Reanimated中给子组件传递布尔选中状态,避免子组件内部做比较?
我的FontPicker组件渲染了一组FontItem子组件,每个FontItem需要接收布尔类型的selected属性。但FontPicker里管控的selectedFont是基于useSharedValue的动画值,想找一种正确的方式把selected属性传递给子组件,不用在每个子组件内部执行选中状态的比较。
父组件代码
const { selectedFont, onPressItem, onScroll, onMomentumScrollEnd, } = useFontPicker(scrollRef, { fonts, activeFont, itemWidth, onPick }); const renderFonts = () => fonts.map((font, index) => { const fontProps = { key: font, fontFamily: font, selectedFont, // <---- 如何避免传递selectedFont?能否只传递基于最新selectedFont.value的布尔属性? style: [fontItemStyles.container, fontItemStyle], textStyle: fontItemTextStyle, onPress: onPressItem(index), }; return <FontItem {...fontProps} />; });
子组件代码
function FontItem({ fontFamily, selectedFont, // <--- 能否改为接收selected布尔属性? style = styles.container, textStyle, onPress, }) { const { colors } = useTheme(); const selected = useDerivedValue(() => selectedFont.value === fontFamily); // <--- 能否在父组件的map中计算这个值? const animatedStyle = useAnimatedStyle(() => { const backgroundColor = selected.value ? colors.onSurface : colors.backdrop; return { backgroundColor }; }); const animatedTextStyle = useAnimatedStyle(() => { const color = selected.value ? colors.primary : colors.onSurface; return { color }; }); const handleOnPress = () => { onPress?.(fontFamily); }; const renderSample = () => ( <Animated.Text style={[{ fontFamily }, animatedTextStyle, textStyle]}> Aa </Animated.Text> ); return ( <TouchableOpacity onPress={handleOnPress}> <Animated.View style={[ animatedStyle, globalStyles.centeredContainer, style, ]} > {renderSample()} </Animated.View> </TouchableOpacity> ); }
解决方案
直接在父组件的map循环里计算布尔值行不通——因为selectedFont.value是Reanimated的共享值,它的更新不会触发React组件重渲染,父组件的map不会重新执行,子组件拿到的selected会一直是旧值。要实现需求,有两种可行方式:
方式一:父组件生成选中状态映射表
在父组件用useDerivedValue创建一个存储所有字体选中状态的共享值映射表,然后给每个子组件传递对应的布尔共享值:
// FontPicker组件内 const selectedMap = useDerivedValue(() => { return fonts.reduce((map, font) => { map[font] = selectedFont.value === font; return map; }, {}); }); const renderFonts = () => fonts.map((font, index) => { const fontProps = { key: font, fontFamily: font, selected: selectedMap.value[font], // 直接传布尔类型的共享值 style: [fontItemStyles.container, fontItemStyle], textStyle: fontItemTextStyle, onPress: onPressItem(index), }; return <FontItem {...fontProps} />; });
修改FontItem组件,直接接收selected共享值:
function FontItem({ fontFamily, selected, // 现在接收布尔类型的共享值 style = styles.container, textStyle, onPress, }) { const { colors } = useTheme(); const animatedStyle = useAnimatedStyle(() => { const backgroundColor = selected.value ? colors.onSurface : colors.backdrop; return { backgroundColor }; }); const animatedTextStyle = useAnimatedStyle(() => { const color = selected.value ? colors.primary : colors.onSurface; return { color }; }); // 其余逻辑保持不变 }
方式二:封装选中状态自定义Hook
如果不想在父组件维护映射表,可以把比较逻辑封装成Hook,让FontItem的props更简洁:
// 自定义Hook function useFontSelected(selectedFont, fontFamily) { return useDerivedValue(() => selectedFont.value === fontFamily); } // FontItem组件内 function FontItem({ fontFamily, selectedFont, style = styles.container, textStyle, onPress, }) { const { colors } = useTheme(); const selected = useFontSelected(selectedFont, fontFamily); // 用Hook封装比较逻辑 // 其余逻辑保持不变 }
这种方式虽然仍需传递selectedFont,但把比较逻辑从组件内抽离,让组件代码更干净。
关键说明
Reanimated的共享值更新不会触发React重渲染,所以绝对不能在父组件的普通JSX渲染逻辑里直接读取selectedFont.value计算布尔值——这样计算出的值不会随selectedFont的更新而变化。必须用useDerivedValue处理共享值的依赖计算,确保选中状态能实时响应动画值变化。
内容的提问来源于stack exchange,提问作者Victorio Jesús Molina Bermejo
相关产品推荐
相关产品推荐

