Реагировать на родную ссылку в элементах Flatlist. Возврат только за последний товар
Я создаю список сворачиваемых объектов, используя компонент реагирования с собственным плоским списком.
Я использую атрибут 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)}
/>
)
}
}
Я также попытался разместить, используя индекс предметов, но не смог заставить его работать.
Есть ли способ обойти это? Я думаю, что ссылка перезаписывается следующей, когда элементы рендеринга.
2 ответа
Вы правы в своем предположении. Ссылка перезаписывается следующим элементом, поэтому ссылка является ссылкой последнего элемента. Вы можете использовать что-то вроде ниже, чтобы установить каждый пункт ссылки отдельно.
ref={(ref) => this.refs[data.id] = ref}
Конечно, это решение предполагает, что у вас есть уникальный идентификатор или сортировка в данных вашего предмета.
Чтобы перефразировать React Native docs, прямое манипулирование (т. Е. Ссылки) следует использовать с осторожностью; если вам это не нужно по какой-то другой причине, о которой я не знаю, в этом случае рефери не нужны. Как правило, лучший способ отслеживать выбранные элементы в FlatList - это использовать keyExtractor
а также extraData
реквизит в сочетании с Javascript Map
объект в состоянии.
React может отслеживать добавляемые / удаляемые / изменяемые элементы, используя уникальные key
prop для каждого элемента (желательно идентификатор или, если необходимо, индексы работают, если порядок списка не изменится). В 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