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

React Native中FlatList项TouchableOpacity onPress响应缓慢求助

优化方案

1. 用Set替代数组存储选中ID,提升查询和更新性能

数组的includes和filter都是O(n)操作,换成Set后,has、add、delete都是O(1),大幅提升状态更新效率:

// 初始化状态为Set
const [selectedPlayers, setSelectedPlayers] = React.useState<Set<string>>(new Set());

// 修改handlePress逻辑
const handlePress = (id: string) => {
  setSelectedPlayers(prev => {
    const newSet = new Set(prev);
    if (newSet.has(id)) {
      newSet.delete(id);
    } else {
      newSet.add(id);
    }
    return newSet;
  });
};

2. 缓存过滤后的players列表,避免重复计算

每次组件渲染时,profiles.filter都会重新执行,用useMemo缓存结果,只在profiles变化时重新计算:

const players = React.useMemo(() => {
  return profiles.filter(profile => profile.role.includes("ATHLETE"));
}, [profiles]);

// 修正原代码中Coaches的过滤条件笔误
const coaches = React.useMemo(() => {
  return profiles.filter(profile => profile.role.includes("COACH"));
}, [profiles]);

3. 修复React.memo失效问题,避免列表项不必要重渲染

原代码中RenderPlayerItem接收的handlePress是每次renderItem时创建的新箭头函数,导致React.memo的浅比较失效,所有列表项都会重渲染。同时把isChecked作为props传入,让组件内部不依赖外部状态:

// 用useCallback缓存handlePress,确保每次渲染函数引用不变
const handlePress = React.useCallback((id: string) => {
  setSelectedPlayers(prev => {
    const newSet = new Set(prev);
    if (newSet.has(id)) {
      newSet.delete(id);
    } else {
      newSet.add(id);
    }
    return newSet;
  });
}, []);

// 修改RenderPlayerItem的props定义
interface RenderItemProps {
  item: IProfile;
  isChecked: boolean;
  onItemPress: (id: string) => void;
}

const RenderPlayerItem = React.memo(
  ({ item, isChecked, onItemPress }: RenderItemProps) => {
    return (
      <TouchableOpacity style={styles.rowContainer} onPress={() => onItemPress(item.id)}>
        <View style={styles.itemRow}>
          <Text>
            {item.lastName.length > 1
              ? `${item.firstName.charAt(0)}.${item.lastName}`
              : item.firstName}
          </Text>
          <CheckboxComponent isChecked={isChecked} />
        </View>
      </TouchableOpacity>
    );
  }
);

// 渲染列表时传入正确的props
<FlatList
  data={players}
  renderItem={({ item }) => (
    <RenderPlayerItem
      item={item}
      isChecked={selectedPlayers.has(item.id)}
      onItemPress={handlePress}
    />
  )}
  keyExtractor={(item) => item.id}
  extraData={selectedPlayers}
  // 开启视图裁剪,优化不可见区域性能
  removeClippedSubviews={true}
  // 控制每次渲染的数量,减少单次渲染压力
  maxToRenderPerBatch={10}
  // 调整窗口大小,预渲染可见区域外的少量项
  windowSize={5}
/>

4. 优化CheckboxComponent(如果自定义组件未优化)

如果CheckboxComponent没有用React.memo包裹,每次状态变化时也会触发不必要的重渲染,建议给CheckboxComponent加上React.memo:

// CheckboxComponent.tsx
export const CheckboxComponent = React.memo(({ isChecked }) => {
  // 组件逻辑
});
优化后的完整代码示例
import React from "react";
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  FlatList,
  TouchableOpacity,
} from "react-native";
import {
  heightPercentageToDP,
  widthPercentageToDP,
} from "react-native-responsive-screen";
import { IProfile } from "../types/cores/profiles";
import { CheckboxComponent } from "../components/cores/checkbox";

interface RenderItemProps {
  item: IProfile;
  isChecked: boolean;
  onItemPress: (id: string) => void;
}

export const CreateTeamPage = ({ route }) => {
  const { profiles } = route.params;
  const [teamName, setTeamName] = React.useState<string>();
  const [selectedPlayers, setSelectedPlayers] = React.useState<Set<string>>(new Set());

  // 缓存过滤后的玩家和教练列表
  const players = React.useMemo(() => {
    return profiles.filter(profile => profile.role.includes("ATHLETE"));
  }, [profiles]);

  const coaches = React.useMemo(() => {
    return profiles.filter(profile => profile.role.includes("COACH"));
  }, [profiles]);

  // 缓存点击事件函数
  const handlePress = React.useCallback((id: string) => {
    setSelectedPlayers(prev => {
      const newSet = new Set(prev);
      if (newSet.has(id)) {
        newSet.delete(id);
      } else {
        newSet.add(id);
      }
      return newSet;
    });
  }, []);

  const RenderPlayerItem = React.memo(
    ({ item, isChecked, onItemPress }: RenderItemProps) => {
      return (
        <TouchableOpacity style={styles.rowContainer} onPress={() => onItemPress(item.id)}>
          <View style={styles.itemRow}>
            <Text>
              {item.lastName.length > 1
                ? `${item.firstName.charAt(0)}.${item.lastName}`
                : item.firstName}
            </Text>
            <CheckboxComponent isChecked={isChecked} />
          </View>
        </TouchableOpacity>
      );
    }
  );

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.headerText}>Create Team</Text>
      </View>
      <Text style={styles.fieldText}>Team name:</Text>
      <TextInput
        onChangeText={(text) => setTeamName(text)}
        value={teamName}
        style={styles.textInput}
      />
      <View style={{ flexDirection: "row" }}>
        <View style={{ width: "50%", height: widthPercentageToDP("50%") }}>
          <Text style={styles.fieldText}>Players:</Text>
          <FlatList
            data={players}
            renderItem={({ item }) => (
              <RenderPlayerItem
                item={item}
                isChecked={selectedPlayers.has(item.id)}
                onItemPress={handlePress}
              />
            )}
            keyExtractor={(item) => item.id}
            extraData={selectedPlayers}
            removeClippedSubviews={true}
            maxToRenderPerBatch={10}
            windowSize={5}
          />
        </View>
      </View>
    </View>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:44:56