You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中如何从JSON数组实现多选项选择功能?

实现多选高亮的解决方案

核心思路

把单选的单个状态值改成数组状态,用来存储所有选中项的索引,通过判断索引是否在数组中来控制高亮,点击时切换选中/取消选中状态。

具体修改步骤

  1. 修改状态定义:将单个选中索引的状态改为数组,初始化为空
// 原代码
const [chooseButton, setChooseButton] = useState();
// 修改后
const [chooseButton, setChooseButton] = useState([]);
  1. 更新点击事件逻辑:点击选项时,切换该选项的选中状态(已选中则移除,未选中则添加),同时更新上下文中的选中技术列表(注意:上下文里的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;
  });
}}
  1. 修改高亮判断逻辑:判断当前项的索引是否在选中数组中,来控制勾选图标和遮罩的显示
// 原高亮判断
{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'}`,
  1. 更新顶部选中项展示:遍历所有选中的技术,动态渲染多个选中按钮
// 原代码
{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>
)}
  1. 更新底部按钮禁用状态:判断选中数组长度是否大于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:30:55