React Native中如何从JSON数组实现多选项选择功能?
实现多选高亮的解决方案
核心思路
把单选的单个状态值改成数组状态,用来存储所有选中项的索引,通过判断索引是否在数组中来控制高亮,点击时切换选中/取消选中状态。
具体修改步骤
- 修改状态定义:将单个选中索引的状态改为数组,初始化为空
// 原代码 const [chooseButton, setChooseButton] = useState(); // 修改后 const [chooseButton, setChooseButton] = useState([]);
- 更新点击事件逻辑:点击选项时,切换该选项的选中状态(已选中则移除,未选中则添加),同时更新上下文中的选中技术列表(注意:上下文里的
techniques需要改为数组类型,否则无法存储多个值)
// 原点击事件 onPress={() => { setChooseButton(i), setTechniques(data.button); }} // 修改后 onPress={() => { setChooseButton(prev => { // 切换选中状态 const newSelected = prev.includes(i) ? prev.filter(idx => idx !== i) : [...prev, i]; // 更新选中的技术列表 const selectedTechniques = newSelected.map(idx => techniqueChooser[idx].button); setTechniques(selectedTechniques); return newSelected; }); }}
- 修改高亮判断逻辑:判断当前项的索引是否在选中数组中,来控制勾选图标和遮罩的显示
// 原高亮判断 {chooseButton === i ? <IconCheck ... /> : ""} // 修改后 {chooseButton.includes(i) ? <IconCheck style={{position:'absolute',right:10,bottom:"10%",zIndex:1}} /> : ""} // 原遮罩显示判断 display: `${chooseButton === i ? 'none' : 'flex'}`, // 修改后 display: `${chooseButton.includes(i) ? 'none' : 'flex'}`,
- 更新顶部选中项展示:遍历所有选中的技术,动态渲染多个选中按钮
// 原代码 {chooseButton !== undefined ? ( <Box style={styles.choosenButtonDynamic}> <Text style={{fontSize: 15, color: '#5D535C'}}> {techniques} </Text> </Box> ) : ( '' )} // 修改后 {chooseButton.length > 0 && ( <Box style={{flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 8}}> {techniques.map((tech, idx) => ( <Box key={idx} style={styles.choosenButtonDynamic}> <Text style={{fontSize: 15, color: '#5D535C'}}> {tech} </Text> </Box> ))} </Box> )}
- 更新底部按钮禁用状态:判断选中数组长度是否大于0
// 原禁用判断 isDisabled={chooseButton !== undefined ? false : true} // 修改后 isDisabled={chooseButton.length === 0} // 原背景色判断 backgroundColor: `${chooseButton !== undefined ? '#36A2D0' : '#6D7A82'}`, // 修改后 backgroundColor: `${chooseButton.length > 0 ? '#36A2D0' : '#6D7A82'}`, // 原图标颜色判断 color: `${chooseButton !== undefined ? 'white' : 'black'}`, // 修改后 color: `${chooseButton.length > 0 ? 'white' : 'black'}`,
完整修改后的代码
import React, {useContext, useState} from 'react'; import { Dimensions, Image, StatusBar, Text, TouchableHighlight, } from 'react-native'; import {Box, Button, ScrollView} from 'native-base'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import {styles} from './createHabitStyles'; import {ApplicationContext} from '../../../context/application_ctx'; import {IconCheck, IconCircleBack} from '../../../svgIcons/icons'; export function ChooseTechnique({navigation, choose}) { // 修改为数组状态存储选中索引 const [chooseButton, setChooseButton] = useState([]); const {habitNavigation, setHabitNavigation, chooseSpan} = useContext(ApplicationContext); const {techniques, setTechniques} = useContext(ApplicationContext); let techniqueChooser = [ {button: 'Mood Journal',src:require('../../../assets/images/techniqueBackgrounds/journal.jpg')}, {button: 'Meditation',src:require('../../../assets/images/techniqueBackgrounds/meditation.jpg')}, {button: 'ABCD',src:require('../../../assets/images/techniqueBackgrounds/abcd.jpg')}, {button: 'Optimism',src:require('../../../assets/images/techniqueBackgrounds/optimism1.jpg')}, {button: 'Journal',src:require('../../../assets/images/techniqueBackgrounds/journal.jpg')}, {button: 'Motivation',src:require('../../../assets/images/techniqueBackgrounds/motivation.jpg')}, {button: 'Defusion',src:require('../../../assets/images/techniqueBackgrounds/defusion.jpg')}, {button: 'Values',src:require('../../../assets/images/techniqueBackgrounds/goals.jpg')}, {button: 'Goals',src:require('../../../assets/images/techniqueBackgrounds/values.jpg')}, {button: 'Thought Record',src:require('../../../assets/images/techniqueBackgrounds/thought.jpg')}, {button: 'Breathe',src:require('../../../assets/images/techniqueBackgrounds/breathe.jpg')}, ]; return ( <> <StatusBar backgroundColor={'#F5F5F5'} translucent={false} barStyle={'dark-content'} /> <Box style={styles.MainBox}> <ScrollView> <Box style={{height: '6%', alignItems: 'center'}}> <Text style={styles.stepperTxt}>Create Habit</Text> </Box> <IconCircleBack onPress={() => setHabitNavigation(0)} style={{...styles.iconBack, top: '2.2%'}} /> <Box style={styles.choosenBox}> <Box style={styles.choosenButton}> <Text style={{fontSize: 15, color: '#5D535C'}}> {chooseSpan.but} </Text> </Box> {/* 动态渲染所有选中的技术 */} {chooseButton.length > 0 && ( <Box style={{flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 8}}> {techniques.map((tech, idx) => ( <Box key={idx} style={styles.choosenButtonDynamic}> <Text style={{fontSize: 15, color: '#5D535C'}}> {tech} </Text> </Box> ))} </Box> )} </Box> <Box style={{height: '6%', marginTop: '2%'}}> <Text style={styles.headTxt}>Choose a technique</Text> <Text style={styles.subHeading}> Create a habit from any one of your subscribed techniques </Text> </Box> <Box style={{height: 1050}}> <Box style={{alignItems: 'center'}}> {techniqueChooser.map((data, i) => { return ( <TouchableHighlight key={i} underlayColor={'none'} onPress={() => { setChooseButton(prev => { // 切换选中状态 const newSelected = prev.includes(i) ? prev.filter(idx => idx !== i) : [...prev, i]; // 更新选中的技术列表 const selectedTechniques = newSelected.map(idx => techniqueChooser[idx].button); setTechniques(selectedTechniques); return newSelected; }); }} style={styles.cardTechniques}> <> {/* 判断是否在选中数组中显示勾选图标 */} {chooseButton.includes(i) ? <IconCheck style={{position:'absolute',right:10,bottom:"10%",zIndex:1}} /> : ""} <Image style={styles.imageOptimism} resizeMethod="scale" resizeMode="cover" source={data.src} /> <Image style={{ ...styles.imageBackdrop, // 判断是否显示遮罩 display: `${chooseButton.includes(i) ? 'none' : 'flex'}`, }} resizeMethod="scale" resizeMode="cover" source={require('../../../assets/images/techniqueBackgrounds/backdrop.png')} /> <Box style={styles.buttonTechnique}> <Text style={{ fontSize: 14, fontWeight: 'bold', color: '#5D535C', }}> {data.button} </Text> </Box> </> </TouchableHighlight> ); })} </Box> </Box> </ScrollView> <Box style={{ position: 'absolute', bottom: '3%', width: '100%', alignItems: 'center', }}> <Button // 根据选中数组长度判断禁用状态 isDisabled={chooseButton.length === 0} style={{ ...styles.button2, backgroundColor: `${ chooseButton.length > 0 ? '#36A2D0' : '#6D7A82' }`, }} isLoadingText="Continue" spinnerPlacement="end" onPress={() =>{ // 原逻辑基于单个选中项,如需处理多个可自行调整 const firstTechnique = techniques[0]; firstTechnique === "Meditation" ? setHabitNavigation(4) : firstTechnique === 'Breathe' ? setHabitNavigation(6) : setHabitNavigation(3) }}> <MaterialIcons name="arrow-forward-ios" style={{ fontSize: 20, color: `${chooseButton.length > 0 ? 'white' : 'black'}`, }} /> </Button> </Box> </Box> </> ); }
注意:上下文
ApplicationContext中的techniques需要改为数组类型,否则无法存储多个选中的技术名称,需要在上下文定义处同步修改。
内容的提问来源于stack exchange,提问作者Ankit Kumar
相关产品推荐
相关产品推荐

