任何状态更改时的 FlatList ScrollView 错误 - 不变违规:不支持动态更改 onViewableItemsChanged

njh*_*jho 25 react-native react-native-flatlist

当应用程序中的状态发生变化时,onViewableItemsChanged似乎不起作用。这样对吗?

如果是这种情况,似乎它不会很有用....

否则,用户将被迫向我们onScroll确定位置或类似的东西......

重现步骤

  1. 请参考小吃
  2. repo也已经上传到github
  3. 使用时任何状态更改都会产生错误 onViewableItemsChanged
  4. 这个错误甚至意味着什么?

注意:将onViewableItemsChanged函数放在const渲染方法的外部也无济于事...

<FlatList
    data={this.state.cardData}
    horizontal={true}
    pagingEnabled={true}
    showsHorizontalScrollIndicator={false}
    onViewableItemsChanged={(info) =>console.log(info)}
    viewabilityConfig={{viewAreaCoveragePercentThreshold: 50}}
    renderItem={({item}) =>
        <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
            <Text>Dogs and Cats</Text>
        </View>
    }
/>
Run Code Online (Sandbox Code Playgroud)

实际行为

错误

图片

Stu*_*ugh 67

基于@woodpav 评论。使用功能组件和 Hook。将viewabilityConfig和分配onViewableItemsChanged给参考并使用它们。像下面这样:

  const onViewRef = React.useRef((viewableItems)=> {
      console.log(viewableItems)
      // Use viewable items in state or as intended
  })
  const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 })


<FlatList
      horizontal={true}
      onViewableItemsChanged={onViewRef.current}
      data={Object.keys(cards)}
      keyExtractor={(_, index) => index.toString()}
      viewabilityConfig={viewConfigRef.current}
      renderItem={({ item, index }) => { ... }}
/>
Run Code Online (Sandbox Code Playgroud)

  • 如果您尝试访问方法内的状态属性,这将不起作用,因为由于 ref 的性质,它不会更新 (5认同)

Raf*_*res 15

Changing onViewableItemsChanged on the fly is not supported发生错误是因为当您更新状态时,您正在创建一个新的onViewableItemsChanged函数引用,因此您正在动态更改它。

虽然 接受的答案可以用 解决问题useRef,但在这种情况下它不是正确的钩子。您应该使用useCallback来返回记忆化的回调并useState获取当前状态,而无需创建对该函数的新引用。

这是一个将所有查看的项目索引保存在状态的示例:

const MyComp = () => {
  const [cardData] = useState(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
  const [viewedItems, setViewedItems] = useState([]);

  const handleVieweableItemsChanged = useCallback(({ changed }) => {
    setViewedItems(oldViewedItems => {
      // We can have access to the current state without adding it
      //  to the useCallback dependencies

      let newViewedItems = null;

      changed.forEach(({ index, isViewable }) => {
        if (index != null && isViewable && !oldViewedItems.includes(index)) {
          
           if (newViewedItems == null) {
             newViewedItems = [...oldViewedItems];
           }
           newViewedItems.push(index);
        }
      });

      // If the items didn't change, we return the old items so
      //  an unnecessary re-render is avoided.
      return newViewedItems == null ? oldViewedItems : newViewedItems;
    });

    // Since it has no dependencies, this function is created only once
  }, []);

  function renderItem({ index, item }) {
    const viewed = '' + viewedItems.includes(index);
    return (
      <View>
        <Text>Data: {item}, Viewed: {viewed}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={cardData}
      onViewableItemsChanged={handleVieweableItemsChanged}
      viewabilityConfig={this.viewabilityConfig}
      renderItem={renderItem}
    />
  );
}
Run Code Online (Sandbox Code Playgroud)

你可以看到它在Snack 上工作。

  • 快速注意:使用这种方法您将失去热重载的功能,因为重载之间的功能会有所不同。 (2认同)

Bil*_*een 9

在 2023 年react-native version 0.71.2,以下代码似乎比旧答案效果更好。


// 1. Define a function outside the component: 
const onViewableItemsChanged = (info) => {
  console.log(info);
};

// 2. create a reference to the function (above)
const viewabilityConfigCallbackPairs = useRef([
  { onViewableItemsChanged },
]);

<FlatList
    data={this.state.cardData}
    horizontal={true}
    pagingEnabled={true}
    showsHorizontalScrollIndicator={false}    
    viewabilityConfig={{viewAreaCoveragePercentThreshold: 50}}
    
    // remove the following statement 
    // onViewableItemsChanged={(info) =>console.log(info)}
    
    // 3. add the following statement, instead of the one above
    viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}

    renderItem={({item}) =>
        <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
            <Text>Dogs and Cats</Text>
        </View>
    }
/>
Run Code Online (Sandbox Code Playgroud)

来源:https ://github.com/facebook/react-native/issues/30171#issuecomment-820833606