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

如何避免修改单个TextInput状态时表单内其他组件重渲染?

解决React组件不必要重渲染问题

我有两个可复用组件AppTextInput和AppButton,为了检测重渲染,在组件内添加了console.log。当前在ChangePasswordScreen表单中,点击CurrentPassword字段的眼睛图标切换密码可见状态时,AppButton和NewPassword对应的AppTextInput也会触发重渲染,需要避免这种不必要的渲染。

原组件代码

AppButton.js

const AppButton = ({
    title = '',
    iconName = '',
    onPress,
    mode = 'contained',
    ...props
}) => {

    console.log("Button rendered")

    return (
        <Button
            icon={iconName}
            mode={mode}
            onPress={onPress}
            {...props}
        >
            {title}
        </Button>
    )
}

AppTextInput.js

const AppTextInput = ({
    inputStyle,
    outlineStyle,
    leftIcon = '',
    rightIcon = '',
    right,
    onRightIconPress,
    label = '',
    ...props
}) => {

    console.log([label, " TextInput Rendered"])

    return (
        <TextInput
            label={label}
            mode='outlined'
            style={[styles.inputStyle, inputStyle]}
            outlineStyle={[styles.outlineStyle, outlineStyle]}
            left={
                leftIcon ? (
                    <TextInput.Icon
                        icon={leftIcon}
                        disabled
                    />
                ) : null
            }
            right={
                rightIcon ? (
                    <TextInput.Icon
                        icon={rightIcon}
                        onPress={onRightIconPress}
                    />
                ) : (
                    right
                )
            }
            {...props}
        />
    )
}

ChangePasswordScreen.js

const ChangePasswordScreen = () => {

    const [currentPasswordSecureText, setCurrentPasswordSecureText] = useState(true);
    const [newPasswordSecureText, setNewPasswordSecureText] = useState(true);

    const [currentPasswordIcon, setCurrentPasswordIcon] = useState("eye-off-outline");
    const [newPasswordIcon, setNewPasswordIcon] = useState("eye-off-outline");

    const showHideCurrentPassword = () => {

        setCurrentPasswordSecureText(!currentPasswordSecureText)

        currentPasswordIcon === "eye-off-outline" ?
            setCurrentPasswordIcon("eye") :
            setCurrentPasswordIcon("eye-off-outline");
    }

    const showHideNewPassword = () => {
        setNewPasswordSecureText(!newPasswordSecureText)

        newPasswordIcon === "eye-off-outline" ?
            setNewPasswordIcon("eye") :
            setNewPasswordIcon("eye-off-outline");
    }

    return (
        <ScrollView
            style={styles.container}
            keyboardShouldPersistTaps='handled'
        >

            <AppTextInput
                label='Current Password'
                autoCapitalize="none"
                autoCorrect={false}
                inputStyle={styles.input}
                leftIcon='lock'
                rightIcon={currentPasswordIcon}
                secureTextEntry={currentPasswordSecureText}
                onRightIconPress={showHideCurrentPassword}
            />

            <AppTextInput
                label='New Password'
                autoCapitalize="none"
                autoCorrect={false}
                inputStyle={styles.input}
                leftIcon='lock'
                rightIcon={newPasswordIcon}
                secureTextEntry={newPasswordSecureText}
                onRightIconPress={showHideNewPassword}
            />

            <AppButton
                title="Update"
                style={styles.button}
            />

        </ScrollView>
    )
}

解决方案

通过React.memo和useCallback结合使用,再优化状态逻辑,即可避免不必要的重渲染:

1. 用React.memo包装子组件

给AppButton和AppTextInput添加React.memo,让组件仅在props发生变化时重渲染:

修改后的AppButton.js:

import React from 'react';

const AppButton = React.memo(({
    title = '',
    iconName = '',
    onPress,
    mode = 'contained',
    ...props
}) => {

    console.log("Button rendered")

    return (
        <Button
            icon={iconName}
            mode={mode}
            onPress={onPress}
            {...props}
        >
            {title}
        </Button>
    )
});

export default AppButton;

修改后的AppTextInput.js:

import React from 'react';

const AppTextInput = React.memo(({
    inputStyle,
    outlineStyle,
    leftIcon = '',
    rightIcon = '',
    right,
    onRightIconPress,
    label = '',
    ...props
}) => {

    console.log([label, " TextInput Rendered"])

    return (
        <TextInput
            label={label}
            mode='outlined'
            style={[styles.inputStyle, inputStyle]}
            outlineStyle={[styles.outlineStyle, outlineStyle]}
            left={
                leftIcon ? (
                    <TextInput.Icon
                        icon={leftIcon}
                        disabled
                    />
                ) : null
            }
            right={
                rightIcon ? (
                    <TextInput.Icon
                        icon={rightIcon}
                        onPress={onRightIconPress}
                    />
                ) : (
                    right
                )
            }
            {...props}
        />
    )
});

export default AppTextInput;

2. 用useCallback缓存父组件回调函数

父组件每次渲染会重新创建showHideCurrentPassword和showHideNewPassword函数,导致子组件的onRightIconPressprops变化,触发重渲染。用useCallback缓存这些函数,确保只有依赖项变化时才生成新函数:

3. 优化状态逻辑

将图标状态由密码可见状态直接计算得到,减少不必要的state,简化逻辑:

修改后的ChangePasswordScreen.js:

import React, { useState, useCallback } from 'react';

const ChangePasswordScreen = () => {

    const [currentPasswordSecure, setCurrentPasswordSecure] = useState(true);
    const [newPasswordSecure, setNewPasswordSecure] = useState(true);

    // 用useCallback缓存回调,依赖项为空(因为使用函数式更新)
    const showHideCurrentPassword = useCallback(() => {
        setCurrentPasswordSecure(prev => !prev);
    }, []);

    const showHideNewPassword = useCallback(() => {
        setNewPasswordSecure(prev => !prev);
    }, []);

    // 根据密码可见状态直接生成图标,无需单独维护图标state
    const currentPasswordIcon = currentPasswordSecure ? "eye-off-outline" : "eye";
    const newPasswordIcon = newPasswordSecure ? "eye-off-outline" : "eye";

    return (
        <ScrollView
            style={styles.container}
            keyboardShouldPersistTaps='handled'
        >

            <AppTextInput
                label='Current Password'
                autoCapitalize="none"
                autoCorrect={false}
                inputStyle={styles.input}
                leftIcon='lock'
                rightIcon={currentPasswordIcon}
                secureTextEntry={currentPasswordSecure}
                onRightIconPress={showHideCurrentPassword}
            />

            <AppTextInput
                label='New Password'
                autoCapitalize="none"
                autoCorrect={false}
                inputStyle={styles.input}
                leftIcon='lock'
                rightIcon={newPasswordIcon}
                secureTextEntry={newPasswordSecure}
                onRightIconPress={showHideNewPassword}
            />

            <AppButton
                title="Update"
                style={styles.button}
            />

        </ScrollView>
    )
}

原理说明

  • React.memo:对组件props进行浅比较,若props未变化则跳过重渲染,减少子组件不必要的渲染。
  • useCallback:缓存函数实例,确保父组件渲染时,传给子组件的回调函数引用不变,避免触发React.memo的重渲染判定。
  • 状态优化:减少state数量,避免因多余状态更新导致的组件渲染,同时让逻辑更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:07:00