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

时间:2017-12-31 21:14:51

标签: react-native react-native-android react-native-ios

当应用程序中存在状态更改时,

onViewableItemsChanged 似乎不起作用。这是对的吗?

如果是这样的话,似乎不会非常有用......

否则,用户将被迫onScroll以确定位置或类似内容......

重现步骤

  1. 请参阅snack
  2. Repo也已在github
  3. 上传
  4. 使用onViewableItemsChanged
  5. 时,任何状态更改都会产生错误
  6. 这个错误甚至意味着什么?
  7. 注意:将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>
                    }
                />
    

    实际行为

    错误

    image

7 个答案:

答案 0 :(得分:5)

基于@woodpav注释,此功能适用于钩子和对我有用的功能组件。分别分配viewabilityConfigonViewableItemsChanged来引用和使用它们。如下所示:

  const onViewRef = React.useRef((viewableItems)=> {console.log(viewableItems)})
  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 }) => { ... }}
>

答案 1 :(得分:3)

发生错误Changing onViewableItemsChanged on the fly is not supported的原因是,当您更新状态时,您正在创建一个新的onViewableItemsChanged函数引用,因此您可以随时对其进行更改。

尽管accepted answer可以解决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}
    />
  );
}

您可以看到它正在Snack上运行。

答案 2 :(得分:2)

您必须将函数传递给组件构造函数中绑定的onViewableItemsChanged,并且必须将viewabilityConfig设置为Flatlist之外的常量。

示例:

class YourComponent extends Component {

    constructor() {
        super()
        this.onViewableItemsChanged.bind(this)
    }

    onViewableItemsChanged({viewableItems, changed}) {
        console.log('viewableItems', viewableItems)
        console.log('changed', changed)
    }

    viewabilityConfig = {viewAreaCoveragePercentThreshold: 50}

    render() {
        return(
          <FlatList
            data={this.state.cardData}
            horizontal={true}
            pagingEnabled={true}
            showsHorizontalScrollIndicator={false}
            onViewableItemsChanged={this.onViewableItemsChanged}
            viewabilityConfig={this.viewabilityConfig}
            renderItem={({item}) =>
                <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
                    <Text>Dogs and Cats</Text>
                 </View>}
          />
        )
    }
}

答案 3 :(得分:0)

将viewabilityConfig对象移动到构造函数。

constructor() {
    this.viewabilityConfig = {
        viewAreaCoveragePercentThreshold: 50
    };
}

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

答案 4 :(得分:0)

Sombody建议使用Flatlist的extraData属性让Flatlist注意到某些更改。

但这对我不起作用,这是对我有用的:

key={this.state.orientation}(例如“人像”或“风景”)时使用orientation ...它可以是您想要的所有内容,但是如果方向发生变化,则必须更改。 如果Flatlist注意到密钥属性已更改,它将重新呈现。

适用于本机0.56

答案 5 :(得分:0)

这对我有用,有没有办法将附加参数传递给onViewRef?像下面的代码一样,如何将类型参数传递给onViewRef。 代码:

        function getScrollItems(items, isPendingList, type) {
    return (
        <FlatList
            data={items}
            style={{width: wp("100%"), paddingLeft: wp("4%"), paddingRight: wp("10%")}}
            horizontal={true}
            keyExtractor={(item, index) => index.toString()}
            showsHorizontalScrollIndicator={false}
            renderItem={({item, index}) => renderScrollItem(item, index, isPendingList, type)}
            viewabilityConfig={viewConfigRef.current}
            onViewableItemsChanged={onViewRef.current}
        />
    )
}

答案 6 :(得分:-1)

将viewabilityConfig prop移除到render函数之外的const值以及onViewableItemsChanged函数