React Native FlatList列表项RadioButton跨项选中方案
React Native FlatList 全局单选按钮实现问题修复
问题现象
使用FlatList渲染带升/降序单选按钮的列表时,仅第一个列表项的单选按钮可正常切换选中状态,其余列表项点击单选按钮无响应,交互要求为任意时刻全局仅可选中一个单选按钮。
问题根因
单选按钮触发状态更新时,仅更新了选中值(asc/desc),未同步记录选中按钮所属列表项的索引值,导致非首项的选中状态判断逻辑无法命中。
原问题代码
import {View, Text, Modal, StyleSheet, Button, FlatList,} from "react-native"; import { RadioButton } from "react-native-paper"; export default class Sortby extends React.Component { constructor(props) { super(props); } state = { selectedIndex: 0, radioButtonValue: 'asc', }; onPress = (index) => { this.setState({ selectedIndex: index, }); } onRadiochange = value => { this.setState({ radioButtonValue: value, }); }; datalist = () => { const gridFieldArray = [ {"text":"Organization","key":"0.6722908010549572"}, {"text":"Document No","key":"0.13707867638770266"}, {"text":"Warehouse","key":"0.5240454674464342"}, {"text":"Business Partner","key":"0.09679684535706568"}, {"text":"Partner Address","key":"0.5778522746749319"}, {"text":"Movement Date","key":"0.40039870656646404"} ] return gridFieldArray; }; render() { return ( <Modal visible={this.props.visible} transparent={true}> <View style={styles.modalStyles}> <View style={styles.fieldsContainer}> <FlatList data={this.datalist()} extraData={this.state} renderItem={(itemData) => { const index = itemData.index; return ( <View style={styles.fieldItem}> <Text style={styles.fieldText}>{itemData.item.text}</Text> <View style={styles.radioButtonContainer}> <RadioButton.Group onValueChange={this.onRadiochange}> <View style={styles.singleRadioButtonContainer}> <Text>Asc</Text> <RadioButton color='#5d86d7' value="asc" status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'asc' ? 'checked' : 'unchecked'} onPress={() => {this.onPress(index)}} /> </View> <View style={styles.singleRadioButtonContainer}> <Text>Desc</Text> <RadioButton color='#5d86d7' value="desc" status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'desc' ? 'checked' : 'unchecked'} onPress={() => {this.onPress(index)}} /> </View> </RadioButton.Group> </View> </View> ); }} alwaysBounceVertical={false} /> </View> <View style={styles.buttonContainer}> <View style={styles.button}> <Button title="OK" color="#5d86d7"></Button> </View> <View style={styles.button}> <Button title="Cancel" color="#5d86d7" onPress={this.props.onCancel} ></Button> </View> </View> </View> </Modal> ); } } const styles = StyleSheet.create({ modalStyles: { height: "auto", width: "90%", flexDirection: "column", justifyContent: "flex-start", alignItems: "center", backgroundColor: "#fff", borderColor: "#777", borderWidth: 1, paddingTop: 15, marginLeft: 20, marginTop: 50, }, fieldsContainer: { width: "100%", }, fieldItem: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingLeft: 12, borderBottomWidth: 1, borderBottomColor: "#ebebeb", }, fieldText: { color: "#444", }, radioButtonContainer: { flexDirection: "row", justifyContent: "flex-start", alignItems: "center", }, singleRadioButtonContainer: { flexDirection: "row", justifyContent: "flex-start", alignItems: "center", marginRight: 10, }, buttonContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 20, marginBottom: 20, }, button: { width: "20%", marginHorizontal: 8, }, });
核心修改点
- 调整
onRadiochange方法入参,同时接收当前列表项索引index和选中值value,状态更新时同步修改selectedIndex和radioButtonValue两个字段,避免索引和选中值不同步 - 渲染列表项时,将当前项的
index透传给RadioButton.Group的onValueChange回调,替代原有仅传递选中值的绑定逻辑,保证点击任意项的单选按钮时,都能正确关联所属列表项
修复后可运行代码
import {View, Text, Modal, StyleSheet, Button, FlatList,} from "react-native"; import { RadioButton } from "react-native-paper"; export default class Sortby extends React.Component { constructor(props) { super(props); } state = { selectedIndex: 0, radioButtonValue: 'asc', }; onPress = (index) => { this.setState({ selectedIndex: index, }); } onRadiochange = (index, value) => { this.setState({ radioButtonValue: value, selectedIndex: index }); }; datalist = () => { const gridFieldArray = [ {"text":"Organization","key":"0.6722908010549572"}, {"text":"Document No","key":"0.13707867638770266"}, {"text":"Warehouse","key":"0.5240454674464342"}, {"text":"Business Partner","key":"0.09679684535706568"}, {"text":"Partner Address","key":"0.5778522746749319"}, {"text":"Movement Date","key":"0.40039870656646404"} ] return gridFieldArray; }; render() { return ( <Modal visible={this.props.visible} transparent={true}> <View style={styles.modalStyles}> <View style={styles.fieldsContainer}> <FlatList data={this.datalist()} extraData={this.state} renderItem={(itemData) => { const index = itemData.index; return ( <View style={styles.fieldItem}> <Text style={styles.fieldText}>{itemData.item.text}</Text> <View style={styles.radioButtonContainer}> <RadioButton.Group onValueChange={value => this.onRadiochange(index, value)}> <View style={styles.singleRadioButtonContainer}> <Text>Asc</Text> <RadioButton color='#5d86d7' value="asc" status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'asc' ? 'checked' : 'unchecked'} onPress={() => {this.onPress(index)}} /> </View> <View style={styles.singleRadioButtonContainer}> <Text>Desc</Text> <RadioButton color='#5d86d7' value="desc" status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'desc' ? 'checked' : 'unchecked'} onPress={() => {this.onPress(index)}} /> </View> </RadioButton.Group> </View> </View> ); }} alwaysBounceVertical={false} /> </View> <View style={styles.buttonContainer}> <View style={styles.button}> <Button title="OK" color="#5d86d7"></Button> </View> <View style={styles.button}> <Button title="Cancel" color="#5d86d7" onPress={this.props.onCancel} ></Button> </View> </View> </View> </Modal> ); } } const styles = StyleSheet.create({ modalStyles: { height: "auto", width: "90%", flexDirection: "column", justifyContent: "flex-start", alignItems: "center", backgroundColor: "#fff", borderColor: "#777", borderWidth: 1, paddingTop: 15, marginLeft: 20, marginTop: 50, }, fieldsContainer: { width: "100%", }, fieldItem: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingLeft: 12, borderBottomWidth: 1, borderBottomColor: "#ebebeb", }, fieldText: { color: "#444", }, radioButtonContainer: { flexDirection: "row", justifyContent: "flex-start", alignItems: "center", }, singleRadioButtonContainer: { flexDirection: "row", justifyContent: "flex-start", alignItems: "center", marginRight: 10, }, buttonContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 20, marginBottom: 20, }, button: { width: "20%", marginHorizontal: 8, }, });
内容的提问来源于stack exchange,提问作者Steph
相关产品推荐
相关产品推荐

