【问题标题】:React Native ref in Flatlist items. Returning for last item alone在 Flatlist 项目中反应 Native ref。单独返回最后一项
【发布时间】:2018-11-25 11:02:38
【问题描述】:

我正在使用 react native flatlist 组件创建一个可折叠列表。

我正在使用 ref 属性来获得点击的项目。

但是当我尝试从 click 事件中访问 ref 时,它不会对单击的项目生效,而是对平面列表中的最后一项生效。

export default class Update extends Component {

renderItems (data, index) {
    return (
        <TouchableNativeFeedback
            onPress={() => this.expandView()}
        >
            <View style={[styles.itemWrapperExpandable]}>
                <View style={styles.itemHeader}>
                    <View style={styles.itemAvatar}>
                        <Image source={require('../images/logo.png')} style={styles.avatar}></Image>
                    </View>
                    <View style={styles.itemContent}>
                        <Text style={[styles.itemTitle, styles.black]}>{data.name}</Text>
                        <Text style={[styles.rating, styles.grey]}>
                            {data.rating}<Icon name="star"></Icon>
                        </Text>
                        <Text style={[styles.content, styles.black]}>{data.description}</Text>
                    </View>
                    <View style={styles.itemBtn}>
                        <Icon name="chevron-down" style={{ color: '#000', fontSize: 22 }}></Icon>
                    </View>
                </View>
                <View ref={(e) => this._expandableView = e } style={[styles.itemBody]}>
                    <Text style={styles.itemBodyText}>
                        some more information about this update will appear here
                        some more information about this update will appear here
                </Text>
                </View>
            </View>
        </TouchableNativeFeedback>
    );
}

expandView () {
    LayoutAnimation.easeInEaseOut();
    if (this._expandableView !== null) {
        if (!this.state.isExpanded) {
            // alert(this.state.isExpanded)
            this._expandableView.setNativeProps({
                style: {height: null, paddingTop: 15, paddingBottom: 15,}
            })
        }
        else {
            this._expandableView.setNativeProps({
                style: {height: 0, paddingTop: 0, paddingBottom: 0,}
            });
        }


        this._expandableView.setState(prevState => ({
            isExpanded: !prevState
        }));
    }
}

render() {
    return (
        <FlatList
            data={this.state.data}
            renderItem={({ item, index }) => this.renderItems(item, index)}
        />
    )
}

}

我也尝试使用项目的索引进行放置,但无法正常工作。

有什么办法吗?我认为在渲染项目时 ref 会被下一个覆盖。

【问题讨论】:

    标签: javascript reactjs react-native ref react-native-flatlist


    【解决方案1】:

    你的假设是对的。 Ref 被下一项覆盖,因此 ref 是最后一项的 ref。您可以使用类似下面的内容分别设置每个项目的引用。

    ref={(ref) => this.refs[data.id] = ref}
    

    当然,此解决方案假定您在项目数据中有一个唯一的 ID 或排序。

    【讨论】:

    • 那么我将如何访问事件处理程序中的 ref?
    • 与分配方式相同,但需要将 id 传递给 expandview 函数
    • this.refs[data.id].setNativeProps 返回未定义
    【解决方案2】:

    为了解释 React Native 文档,应该谨慎使用直接操作(即 refs);除非您出于我不知道的其他原因需要它,否则在这种情况下不需要参考。通常,跟踪 FlatList 中选定项目的最佳方法是利用 keyExtractorextraData 属性以及状态中的 Javascript Map 对象。

    React 能够跟踪正在添加/删除/修改的项目的方式是为每个项目使用一个唯一的 key 属性(最好是一个 id,或者如果列表顺序不会改变,如果有必要的索引工作)。在 FlatList 中,如果您将使用 keyExtractor 属性,则会“自动”处理。为了跟踪选定的项目,我们可以在我们点击一​​个项目时从我们的 Map 对象中添加/删除项目。 Map 是一种对象类型,例如保存键值对的数组。我们将在 state 中使用它来为每个被选中的项目存储一个键 item.id 和一个布尔值 true

    所以,我们最终会得到这样的结果:

    export default class Update extends Component {
      state = {
        data: [],
        selected: (new Map(): Map<string, boolean>)
      }
    
      renderItems = ({ item }) => {
        // note: the double ! operator is to make sure non boolean values are properly converted to boolean
        return (
          <ExpandableItem
            item={item}
            selected={!!this.state.selected.get(item.id)}
            expandView={() => this.expandView(item)}
          />
        );
      }
    
      expandView (item) {
        LayoutAnimation.easeInEaseOut();
    
        this.setState((state) => {
          const selected = new Map(state.selected);
          selected.set(item.id, !selected.get(item.id));
          return {selected};
        });
    
        // the above allows for multiple expanded items at a time; the following will simultaneously close the last item when expanding a new one
        // this.setState((state) => {
        //   const selected = new Map();
        //   selected.set(item.id, true);
        //   return {selected};
        // });
      }
    
      render() {
        return (
          <FlatList
            data={this.state.data}
            keyExtractor={(item, index) => `${item.id}`}
            renderItem={this.renderItems}
          />
        );
      }
    }
    
    const ExpandableItem = ({ item, selected, expandView }) => {
      return (
        <TouchableNativeFeedback onPress={expandView}>
          <View style={styles.itemWrapperExpandable}>
            {/* ...insert other header code */}
            <View style={[styles.itemBody, selected && styles.itemBodySelected]}>
              <Text style={styles.itemBodyText}>
                some more information about this update will appear here
              </Text>
            </View>
          </View>
        </TouchableNativeFeedback>
      );
    }

    您必须使用styles.itemBodySelected 才能使其看起来像您想要的那样。请注意,renderItem 的单独功能组件 &lt;ExpandableItem /&gt; 不是必需的,这只是我喜欢的代码结构。

    有用的链接:

    https://facebook.github.io/react-native/docs/flatlist.html

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

    https://reactjs.org/docs/lists-and-keys.html#keys

    【讨论】:

    • 这看起来很准确,但没有用。我追踪了它,并且没有设置选定的状态。当我在 expandView 方法中记录 selected.get(item.id) 时,它什么也没返回
    • 选中状态实际改变了,但在组件中没有生效
    • 你能帮我看看这个吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    • 2019-03-17
    相关资源
    最近更新 更多