You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:30:10