React Native ref в элементах Flatlist. Возврат только за последний товар

Я создаю список сворачиваемых, используя собственный компонент плоского списка react.

Я использую атрибут 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)}
        />
    )
}

}

Я также пробовал размещать, используя индекс элементов, но не смог заставить его работать.

Каким-либо образом обойти это? Я думаю, что ссылка перезаписывается следующей при рендеринге элементов.


person Isaac Oluwatemilorun    schedule 15.06.2018    source источник


Ответы (2)


Вы правы в своем предположении. Ref перезаписывается следующим элементом, поэтому ref - это ссылка последнего элемента. Вы можете использовать что-то вроде ниже, чтобы установить каждый элемент по отдельности.

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

Конечно, это решение предполагает, что у вас есть уникальный идентификатор или сортировка в данных элемента.

person bennygenel    schedule 15.06.2018
comment
Итак, как мне получить доступ к ссылке в обработчике событий? - person Isaac Oluwatemilorun; 15.06.2018
comment
То же, что вы назначаете, но вам нужно передать идентификатор функции expandview - person bennygenel; 15.06.2018
comment
this.refs [data.id] .setNativeProps возвращает undefined - person Isaac Oluwatemilorun; 16.06.2018

Перефразируя документацию React Native, прямые манипуляции (т.е. ссылки) следует использовать с осторожностью; Если вам это не нужно по какой-либо другой причине, о которой я не знаю, в этом случае ссылки не нужны. Как правило, лучший способ отслеживать выбранные элементы в FlatList - использовать свойства keyExtractor и extraData в сочетании с объектом Javascript Map в состоянии.

Способ, которым React может отслеживать добавляемые / удаляемые / изменяемые элементы, заключается в использовании уникальной key опоры для каждого элемента (желательно идентификатора, или, если необходимо, работают индексы, если порядок в списке не изменится). В FlatList это обрабатывается «автоматически», если вы будете использовать свойство keyExtractor. Чтобы отслеживать выбранный элемент, мы можем добавлять / удалять элементы из нашего объекта Map всякий раз, когда мы нажимаем на один из них. Карта - это тип объекта, подобный массиву, который содержит пары ключ-значение. Мы будем использовать это в состоянии для хранения ключа 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, чтобы он выглядел так, как вы хотите. Обратите внимание, что отдельный функциональный компонент <ExpandableItem /> для renderItem не требуется, это просто то, как я предпочитаю структурировать свой код.

Полезные ссылки:

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

person swimisbell    schedule 15.06.2018
comment
Это кажется точным, но не сработало. Я отследил это, и выбранное состояние не установилось. Когда я зарегистрировал selected.get (item.id) в методе expandView, он ничего не вернул - person Isaac Oluwatemilorun; 16.06.2018
comment
Выбранное состояние фактически изменилось, но не вступило в силу в компоненте. - person Isaac Oluwatemilorun; 16.06.2018
comment
можешь пройти через это для меня? - person Isaac Oluwatemilorun; 16.06.2018
comment
можешь пройти через это для меня? - person Isaac Oluwatemilorun; 16.06.2018