首页 > 解决方案 > 子组件未在道具更改时重新渲染

问题描述

在以下代码中,我希望OfferList在将商品添加到商店时重新呈现。OfferList本身不是可观察的,但报价数组作为道具传递。

export const MerchantScreen: FC = observer(() => {
  const { merchantStore } = useStores()

  return (
    <View>
      <OfferList data={merchantStore.offers} />
      <View>
        <Button title={"New Offer"} onPress={() => merchantStore.addOffer()}/>
      </View>
    </View>
  )
})

export const OfferList: FC<OfferListProps> = ({ data }: OfferListProps) => {
  const renderItem = (offer: ListRenderItemInfo<any>) => {
    return (
      <Text>{offer.name}</Text>
    )
  }

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
    />
  )
}

我使用 Mobx 状态树。现在merchantStore.addOffer()要做的就是将另一个报价项推送到数组中。

我尝试了什么/发现:

当我从我的商店阅读时MerchantScreen,例如通过添加

<Text>{ merchantStore.offers.toString() }</Text>

OfferList也会更新。我怀疑直接在父组件中从商店读取也会强制重新渲染子组件。

我在这里偶然发现了一些答案,这表明keyFlatList renderItems 中缺少的属性可能是问题所在。尝试使用key={item.id}无济于事。另外,如您所见,我使用了 FlatList 的 keyExtractor 属性。

另一个答案建议将本地状态引入组件,如下所示:

export const OfferList: FC<OfferListProps> = ({ data }: OfferListProps) => {
  const [offers, setOfferss] = useState()

  useEffect(() => {
    setOffers(data)
  }, [data])
  
  const renderItem = (offer: ListRenderItemInfo<any>) => {
    return (
      <Text>{offer.name}</Text>
    )
  }

  return (
    <FlatList
      data={offers}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
    />
  )
}

这不起作用,我的直觉是这不是它的完成方式。

如您所见,我的MerchantScreen父组件是可观察的,而我的子组件OfferList不是。根据我的期望,observer我的父组件应该足够了。父组件应该已经检测到存储中的更改并重新渲染。子组件本身甚至不使用商店。

总的来说,手头的问题似乎很微不足道,所以我想我只是错过了一个重要的细节。

标签: react-nativemobxmobx-state-tree

解决方案


MobX 只跟踪观察者组件访问的数据,如果它们被渲染直接访问,所以如果你想对每一个做出反应,offers你需要在某个地方访问它们。当你尝试时,你有点做merchantStore.offers.toString(),这就是它起作用的原因。

所以首先你需要制作OfferList一个observer.

但是你有FlatList哪个是本机组件,你不能把它变成一个observer. 你可以做的是访问offers里面的每个项目OfferList(基本上只是为了订阅更新),data={offers.slice()}或者使用 MobX 辅助方法更好toJS data={toJS(offers)}

根据您的用例,您可能还想<Observer>renderItem回调中使用:

  const renderItem = (offer: ListRenderItemInfo<any>) => {
    return (
      <Observer>{() => <Text>{offer.name}</Text>}</Observer>
    )
  }

推荐阅读