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

React Native Expo FlatList含输入框焦点跳转问题求助

React Native FlatList输入框焦点跳转问题优化方案

问题背景

使用React Native Expo开发的商品列表页面,通过FlatList展示商品,每个列表项包含数量输入框。因FlatList组件复用机制,在键盘关闭时点击底部项的输入框,焦点会随机跳转到顶部某输入框;禁用removeClippedSubviews虽能解决,但列表项增多后会大幅卡顿,需更优方案。

核心原因

FlatList的组件复用会导致旧组件的状态(包括输入框焦点、本地state)被新的列表项继承,当购物车状态更新或列表滚动时,组件状态未及时与当前商品绑定,引发焦点错乱。

优化方案

1. 给FlatList添加extraData属性

FlatList默认仅监听data数组的变化,添加extraData后,当购物车状态改变时,FlatList会重新渲染对应列表项,确保组件状态与当前商品匹配。

修改Catalogue.tsx中的FlatList配置:

import {useAppSelector} from "@src/hooks";

const renderProducts = () => {
    // 获取购物车列表作为extraData的依赖
    const cartList = useAppSelector(state => state.cartSlice.list);
    
    return (
        <FlatList
            ref={listRef}
            key={userHasRoleBusiness ? 'single' : 'double'}
            data={products}
            renderItem={renderItem}
            keyExtractor={(item) => item.id.toString()}
            numColumns={userHasRoleBusiness ? 1 : 2}
            contentContainerStyle={flatListStyle}
            ListHeaderComponent={renderHeader}
            ListEmptyComponent={renderEmptyList}
            onScroll={userHasRoleBusiness ? handleScroll : undefined}
            scrollEventThrottle={16}
            keyboardShouldPersistTaps="handled"
            removeClippedSubviews={true} // 恢复组件复用,保证性能
            extraData={cartList} // 新增:让FlatList感知购物车状态变化
        />
    );
};

2. 监听product变化重置输入框状态

当FlatList复用组件时,product属性会切换为新商品,此时需重置输入框的数量值和焦点状态,避免旧数据残留。

修改AddQuantityInput组件:

const AddQuantityInput = forwardRef<AddQuantityInputRef, QuantityInputProps>(({product, removeDelayMs = 0}, ref) => {
    const removeTimerRef = useRef<NodeJS.Timeout | null>(null);
    const dispatch = useAppDispatch();
    const inputRef = useRef<TextInput>(null);
    const [quantity, setQuantity] = useState('');

    // 初始化或product变化时同步购物车数量
    useEffect(() => {
        const currentQty = useAppSelector((state) =>
            state.cartSlice.list.find(item => item.product.id === product.id)
        )?.quantity ?? 0;
        setQuantity(currentQty > 0 ? String(currentQty) : '');
        
        // 如果输入框当前处于聚焦状态,强制失焦避免焦点残留
        if (inputRef.current?.isFocused()) {
            inputRef.current.blur();
        }
    }, [product]);

    // ... 原有handleQuantityChange等逻辑保持不变

    return (
        // ... 原有JSX结构保持不变
    );
});

3. 集中管理输入框Ref(可选)

在父组件维护每个商品输入框的Ref映射,确保点击操作能精准定位到当前商品的输入框,彻底避免复用导致的Ref指向错误。

步骤1:修改Catalogue.tsx,添加Ref映射

const inputRefs = useRef<Map<string, AddQuantityInputRef>>(new Map());

const renderItem = ({item}: { item: Product }) => {
    return (
        <View style={{width: userHasRoleBusiness ? '100%' : '50%', paddingHorizontal: 8}}>
            <ProductItem
                product={item}
                isAdmin={userHasRoleAdmin}
                isBusinessUser={userHasRoleBusiness}
                onChangePrice={onChangePrice}
                // 传递Ref回调,将当前商品的输入框Ref存入映射
                inputRef={(ref) => {
                    const productId = item.id.toString();
                    if (ref) {
                        inputRefs.current.set(productId, ref);
                    } else {
                        inputRefs.current.delete(productId);
                    }
                }}
            />
        </View>
    );
};

步骤2:更新ProductItem组件的Props

type ProductItemProps = {
    product: Product;
    isAdmin: boolean;
    isBusinessUser: boolean;
    onChangePrice: (product: Product, price: number) => Promise<void>;
    inputRef: (ref: AddQuantityInputRef | null) => void; // 新增Ref回调属性
};

const ProductItem = ({ product, inputRef }: ProductItemProps) => {
    return (
        <AddQuantityInput ref={inputRef} product={product} />
    );
};

效果验证

恢复removeClippedSubviews={true}后,列表滚动性能正常,点击底部输入框时焦点不会跳转到顶部,输入操作正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 13:19:55