在 React-Native FlatList 中高亮选中的项目
Highlight a selected item in React-Native FlatList
我组装了一个简单的 React-native 应用程序来从远程服务获取数据,并将其加载到 FlatList 中。当用户点击一个项目时,它应该被突出显示并且选择应该被保留。我相信这样一个微不足道的操作应该不难。我不确定我错过了什么。
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
Image,
TouchableOpacity,
} from 'react-native';
export default class BasicFlatList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
selectedItem:'null',
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, seed} = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({loading: true});
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({error, loading: false});
});
};
onPressAction = (rowItem) => {
console.log('ListItem was selected');
console.dir(rowItem);
this.setState({
selectedItem: rowItem.id.value
});
}
renderRow = (item) => {
const isSelectedUser = this.state.selectedItem === item.id.value;
console.log(`Rendered item - ${item.id.value} for ${isSelectedUser}`);
const viewStyle = isSelectedUser ? styles.selectedButton : styles.normalButton;
return(
<TouchableOpacity style={viewStyle} onPress={() => this.onPressAction(item)} underlayColor='#dddddd'>
<View style={styles.listItemContainer}>
<View>
<Image source={{ uri: item.picture.large}} style={styles.photo} />
</View>
<View style={{flexDirection: 'column'}}>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
{isSelectedUser ?
<Text style={styles.selectedText}>{item.name.first} {item.name.last}</Text>
: <Text style={styles.text}>{item.name.first} {item.name.last}</Text>
}
</View>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
<Text style={styles.text}>{item.email}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
},
selectedButton: {
backgroundColor: 'lightgray',
},
normalButton: {
backgroundColor: 'white',
},
listItemContainer: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'flex-start',
},
text: {
marginLeft: 12,
fontSize: 16,
},
selectedText: {
marginLeft: 12,
fontSize: 20,
},
photo: {
height: 40,
width: 40,
borderRadius: 20,
},
});
当用户点击列表中的项目时,将调用“onPress”方法并显示所选项目的信息。但是 Flatlist 中高亮项目的下一步不会发生。 'UnderlayColor'也没有用。
任何 help/advice 将不胜感激。
您应该将 extraData 道具传递给您的 FlatList,以便它会根据您的选择重新呈现您的项目
此处:
<FlatList style={styles.container}
data={this.state.data}
extraData={this.state.selectedItem}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
来源:https://facebook.github.io/react-native/docs/flatlist
Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes
代替 this.state.selectedItem
并为 rowItem.id.value
设置 with/checking,我建议使用具有 key:value 对的 Map 对象,如 RN FlatList 文档示例中所示: https://facebook.github.io/react-native/docs/flatlist.html. Take a look at the js Map docs as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
@j.I-V 推荐的 extraData 属性将确保在 this.state.selected 选择发生变化时重新渲染。
您的 onPressAction 显然会与下面的示例有所不同,具体取决于您是想在任何给定时间限制选择的数量还是不允许用户切换选择等。
另外,虽然没有必要,但我喜欢为 renderItem
组件使用另一个 class 或纯组件;最终看起来像下面这样:
export default class BasicFlatList extends Component {
state = {
otherStateStuff: ...,
selected: (new Map(): Map<string, boolean>) //iterable object with string:boolean key:value pairs
}
onPressAction = (key: string) => {
this.setState((state) => {
//create new Map object, maintaining state immutability
const selected = new Map(state.selected);
//remove key if selected, add key if not selected
this.state.selected.has(key) ? selected.delete(key) : selected.set(key, !selected.get(key));
return {selected};
});
}
renderRow = (item) => {
return (
<RowItem
{...otherProps}
item={item}
onPressItem={this.onPressAction}
selected={!!this.state.selected.get(item.key)} />
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
extraData={this.state}
/>
);
}
}
class RowItem extends Component {
render(){
//render styles and components conditionally using this.props.selected ? _ : _
return (
<TouchableOpacity onPress={this.props.onPressItem}>
...
</TouchableOpacity>
)
}
}
您可以这样做:
对于 renderItem,使用类似 TouchableOpacity 的东西和 onPress 事件传递 renderedItem 的索引或 id;
将所选项目添加到状态的函数:
handleSelection = (id) => {
var selectedId = this.state.selectedId
if(selectedId === id)
this.setState({selectedItem: null})
else
this.setState({selectedItem: id})
}
handleSelectionMultiple = (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
this.setState({selectedIds})
}
平面列表:
<FlatList
data={data}
extraData={
this.state.selectedId // for single item
this.state.selectedIds // for multiple items
}
renderItem={(item) =>
<TouchableOpacity
// for single item
onPress={() => this.handleSelection(item.id)}
style={item.id === this.state.selectedId ? styles.selected : null}
// for multiple items
onPress={() => this.handleSelectionMultiple(item.id)}
style={this.state.selectedIds.includes(item.id) ? styles.selected : null}
>
<Text>{item.name}</Text>
</TouchableOpacity>
}
/>
为所选项目制作样式即可!
第一个
constructor() {
super();
this.state = {
selectedIds:[]
};
}
第二
handleSelectionMultiple = async (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
await this.setState({selectedIds})
}
第三
<CheckBox
checked={this.state.selectedIds.includes(item.expense_detail_id) ? true : false}
onPress={()=>this.handleSelectionMultiple(item.expense_detail_id)}
/>
最后我从 Maicon Gilton 给出的答案中得到了我的问题的解决方案
我组装了一个简单的 React-native 应用程序来从远程服务获取数据,并将其加载到 FlatList 中。当用户点击一个项目时,它应该被突出显示并且选择应该被保留。我相信这样一个微不足道的操作应该不难。我不确定我错过了什么。
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
Image,
TouchableOpacity,
} from 'react-native';
export default class BasicFlatList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
selectedItem:'null',
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, seed} = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({loading: true});
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({error, loading: false});
});
};
onPressAction = (rowItem) => {
console.log('ListItem was selected');
console.dir(rowItem);
this.setState({
selectedItem: rowItem.id.value
});
}
renderRow = (item) => {
const isSelectedUser = this.state.selectedItem === item.id.value;
console.log(`Rendered item - ${item.id.value} for ${isSelectedUser}`);
const viewStyle = isSelectedUser ? styles.selectedButton : styles.normalButton;
return(
<TouchableOpacity style={viewStyle} onPress={() => this.onPressAction(item)} underlayColor='#dddddd'>
<View style={styles.listItemContainer}>
<View>
<Image source={{ uri: item.picture.large}} style={styles.photo} />
</View>
<View style={{flexDirection: 'column'}}>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
{isSelectedUser ?
<Text style={styles.selectedText}>{item.name.first} {item.name.last}</Text>
: <Text style={styles.text}>{item.name.first} {item.name.last}</Text>
}
</View>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
<Text style={styles.text}>{item.email}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
},
selectedButton: {
backgroundColor: 'lightgray',
},
normalButton: {
backgroundColor: 'white',
},
listItemContainer: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'flex-start',
},
text: {
marginLeft: 12,
fontSize: 16,
},
selectedText: {
marginLeft: 12,
fontSize: 20,
},
photo: {
height: 40,
width: 40,
borderRadius: 20,
},
});
当用户点击列表中的项目时,将调用“onPress”方法并显示所选项目的信息。但是 Flatlist 中高亮项目的下一步不会发生。 'UnderlayColor'也没有用。
任何 help/advice 将不胜感激。
您应该将 extraData 道具传递给您的 FlatList,以便它会根据您的选择重新呈现您的项目
此处:
<FlatList style={styles.container}
data={this.state.data}
extraData={this.state.selectedItem}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
来源:https://facebook.github.io/react-native/docs/flatlist
Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes
代替 this.state.selectedItem
并为 rowItem.id.value
设置 with/checking,我建议使用具有 key:value 对的 Map 对象,如 RN FlatList 文档示例中所示: https://facebook.github.io/react-native/docs/flatlist.html. Take a look at the js Map docs as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
@j.I-V 推荐的 extraData 属性将确保在 this.state.selected 选择发生变化时重新渲染。
您的 onPressAction 显然会与下面的示例有所不同,具体取决于您是想在任何给定时间限制选择的数量还是不允许用户切换选择等。
另外,虽然没有必要,但我喜欢为 renderItem
组件使用另一个 class 或纯组件;最终看起来像下面这样:
export default class BasicFlatList extends Component {
state = {
otherStateStuff: ...,
selected: (new Map(): Map<string, boolean>) //iterable object with string:boolean key:value pairs
}
onPressAction = (key: string) => {
this.setState((state) => {
//create new Map object, maintaining state immutability
const selected = new Map(state.selected);
//remove key if selected, add key if not selected
this.state.selected.has(key) ? selected.delete(key) : selected.set(key, !selected.get(key));
return {selected};
});
}
renderRow = (item) => {
return (
<RowItem
{...otherProps}
item={item}
onPressItem={this.onPressAction}
selected={!!this.state.selected.get(item.key)} />
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
extraData={this.state}
/>
);
}
}
class RowItem extends Component {
render(){
//render styles and components conditionally using this.props.selected ? _ : _
return (
<TouchableOpacity onPress={this.props.onPressItem}>
...
</TouchableOpacity>
)
}
}
您可以这样做:
对于 renderItem,使用类似 TouchableOpacity 的东西和 onPress 事件传递 renderedItem 的索引或 id;
将所选项目添加到状态的函数:
handleSelection = (id) => { var selectedId = this.state.selectedId if(selectedId === id) this.setState({selectedItem: null}) else this.setState({selectedItem: id}) } handleSelectionMultiple = (id) => { var selectedIds = [...this.state.selectedIds] // clone state if(selectedIds.includes(id)) selectedIds = selectedIds.filter(_id => _id !== id) else selectedIds.push(id) this.setState({selectedIds}) }
平面列表:
<FlatList data={data} extraData={ this.state.selectedId // for single item this.state.selectedIds // for multiple items } renderItem={(item) => <TouchableOpacity // for single item onPress={() => this.handleSelection(item.id)} style={item.id === this.state.selectedId ? styles.selected : null} // for multiple items onPress={() => this.handleSelectionMultiple(item.id)} style={this.state.selectedIds.includes(item.id) ? styles.selected : null} > <Text>{item.name}</Text> </TouchableOpacity> }
/>
为所选项目制作样式即可!
第一个
constructor() {
super();
this.state = {
selectedIds:[]
};
}
第二
handleSelectionMultiple = async (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
await this.setState({selectedIds})
}
第三
<CheckBox
checked={this.state.selectedIds.includes(item.expense_detail_id) ? true : false}
onPress={()=>this.handleSelectionMultiple(item.expense_detail_id)}
/>
最后我从 Maicon Gilton 给出的答案中得到了我的问题的解决方案