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

React Native中16个TouchableOpacity独立控制与已点击按钮禁用问题

嘿,这个问题其实很好解决——你现在所有盒子共用一个全局的pressed状态,所以一点击全都会“同步响应”。咱们分两种靠谱的方式来实现每个盒子独立控制点击逻辑,并且点击后自动禁用:


方法一:用数组维护每个盒子的禁用状态(类组件版)

这种方式适合把所有状态集中管理,逻辑清晰直观:

1. 初始化每个盒子的状态

在组件的构造函数里,用一个数组来记录16个盒子的禁用状态,初始值全为false(表示都未点击):

constructor(props) {
  super(props);
  this.state = {
    music: [...], // 你的音乐列表
    disabledBoxes: Array(16).fill(false) // 每个盒子对应一个禁用状态
  };
}

2. 修改点击函数,让它区分不同盒子

给onTap函数加一个索引参数,点击时只更新对应盒子的禁用状态,而不是全局状态:

onTap = (index) => {
  // 先判断当前盒子是否已经被禁用
  if (!this.state.disabledBoxes[index]) {
    // 复制状态数组,避免直接修改原数组(React状态不可变原则)
    const newDisabledBoxes = [...this.state.disabledBoxes];
    newDisabledBoxes[index] = true;
    
    this.setState({ disabledBoxes: newDisabledBoxes }, () => {
      // 状态更新完成后,再执行打开音乐的逻辑
      const rastgele = Math.floor(Math.random() * this.state.music.length);
      Linking.openURL(this.state.music[rastgele]);
      console.warn('pressed box:', index + 1, 'music index:', rastgele);
    });
  }
};

3. 渲染时给每个盒子绑定索引和禁用状态

循环渲染16个盒子,把对应索引传给onTap,并根据disabledBoxes数组设置按钮的disabled属性:

{Array.from({ length: 16 }).map((_, index) => (
  <View style={{ /* 你的盒子容器样式 */ }} key={index}>
    <TouchableOpacity
      onPress={() => this.onTap(index)}
      style={{ /* 你的按钮样式 */ }}
      disabled={this.state.disabledBoxes[index]}
    >
      <Text style={styles.textStyle}> {index + 1} </Text>
    </TouchableOpacity>
  </View>
))}

方法二:封装独立的盒子组件(更模块化)

如果后续每个盒子可能需要更多独立逻辑,把盒子封装成单独组件会更灵活,每个组件自己维护自己的状态:

1. 创建独立的Box组件

import React, { Component } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import Linking from 'react-native/Libraries/Linking/Linking';

class Box extends Component {
  state = {
    pressed: false // 每个盒子自己的点击状态
  };

  onTap = () => {
    if (!this.state.pressed) {
      this.setState({ pressed: true }, () => {
        const rastgele = Math.floor(Math.random() * this.props.music.length);
        Linking.openURL(this.props.music[rastgele]);
        console.warn('pressed box:', this.props.number, 'music index:', rastgele);
      });
    }
  };

  render() {
    return (
      <View style={{ /* 你的盒子容器样式 */ }}>
        <TouchableOpacity
          onPress={this.onTap}
          style={{ /* 你的按钮样式 */ }}
          disabled={this.state.pressed}
        >
          <Text style={styles.textStyle}> {this.props.number} </Text>
        </TouchableOpacity>
      </View>
    );
  }
}

export default Box;

2. 在父组件中渲染16个Box

{Array.from({ length: 16 }).map((_, index) => (
  <Box key={index} number={index + 1} music={this.state.music} />
))}

补充:函数组件版的实现(如果用Hook)

如果你用的是React函数组件,用useState就能轻松实现:

import React, { useState } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import Linking from 'react-native/Libraries/Linking/Linking';

const BoxContainer = ({ music }) => {
  const [disabledBoxes, setDisabledBoxes] = useState(Array(16).fill(false));

  const onTap = (index) => {
    if (!disabledBoxes[index]) {
      const newDisabledBoxes = [...disabledBoxes];
      newDisabledBoxes[index] = true;
      setDisabledBoxes(newDisabledBoxes);
      
      const rastgele = Math.floor(Math.random() * music.length);
      Linking.openURL(music[rastgele]);
      console.warn('pressed box:', index + 1, 'music index:', rastgele);
    }
  };

  return (
    <View style={{ /* 容器样式 */ }}>
      {Array.from({ length: 16 }).map((_, index) => (
        <View style={{ /* 盒子样式 */ }} key={index}>
          <TouchableOpacity
            onPress={() => onTap(index)}
            style={{ /* 按钮样式 */ }}
            disabled={disabledBoxes[index]}
          >
            <Text style={{ /* 文本样式 */ }}>{index + 1}</Text>
          </TouchableOpacity>
        </View>
      ))}
    </View>
  );
};

export default BoxContainer;

两种方法都能解决你的问题,选哪种看你后续的需求复杂度~

内容的提问来源于stack exchange,提问作者Berker Topuz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:36:57