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

React父组件重渲染时如何保留子组件状态?

问题描述

当catName或catWeight输入框更新时,父组件会触发重渲染,导致categories列表中的子组件<Category>也跟着重渲染,而且我尝试用React.memo()包装子组件却没有效果。现在想保留子组件的状态,应该选择保留子组件同时允许父组件重渲染,还是完全阻止父组件重渲染?具体该怎么操作?

const [catName, setcatName] = useState('')
const [catWeight, setcatWeight] = useState('')
const [categories, setCategories] = useState([]);
let allGood = false;
const handleSubmit = (e) => {
    e.preventDefault()
    if(catWeight <= 100) {
        allGood = true
    }
    if(catWeight < 1) {
        allGood = false
    }
    if(allGood) {
        setCategories([{ catName, catWeight }, ...categories])
    } else alert("Something went wrong!")
    
}
...
return (
    <div>
      <form onSubmit={handleSubmit}>
        <label>
            Enter Category Name:
            <input
                required
                autoCapitalize="words"
                autoComplete="off"
                type="text"
                name="cat-name"
                value={catName}
                onChange={
                    (e) => {setcatName(e.target.value)
                    e.preventDefault()}
                }
            />
            <br/>
            Enter Category Weight:
            <input
                required
                autoComplete="off"
                type="number"
                name="cat-name"
                value={catWeight}
                onChange={
                    (e) => setcatWeight(e.target.value)
                }  
            />
        </label>
        <input type="submit" value="Submit"/>
      </form>
    
    {/* displaying new category */}
    <div id="display-categories">
        {categories.map(({ catName, catWeight }) => (
            <Category key={uuid()} name={catName} weight={catWeight}/>
        ))}
    </div>

    </div>
);

问题分析

你用React.memo没效果的核心原因是渲染时动态生成的key:每次父组件重渲染,uuid()都会生成全新的key值,React会判定这些key对应的是全新组件,直接销毁旧的<Category>实例并创建新的,导致子组件状态丢失,React.memo的浅比较完全起不到作用。


解决方案

方案一:保留子组件状态,允许父组件正常重渲染(推荐)

父组件因输入框更新重渲染是正常行为,我们只需要避免子组件被无意义地销毁重建,同时利用React.memo跳过不必要的重渲染。

1. 给每个category添加稳定唯一的ID

在添加新分类时就生成唯一ID,而不是在渲染时动态生成:

// 修改handleSubmit中的setCategories逻辑
if(allGood) {
    // 生成一次ID,后续不再变化
    const newCategory = {
        id: uuid(),
        catName,
        catWeight
    }
    setCategories([newCategory, ...categories])
}

2. 渲染时使用稳定的key

{categories.map(({ id, catName, catWeight }) => (
    <Category key={id} name={catName} weight={catWeight}/>
))}

3. 用React.memo包裹子组件

确保子组件接收的props是基础类型(字符串、数字),这样浅比较能生效:

const Category = React.memo(({ name, weight }) => {
    // 子组件内部状态会被保留,props不变时不会重渲染
    return (
        <div>{name} - {weight}</div>
    )
})

这样父组件重渲染时,只要子组件的name和weight没变化,React.memo就会跳过子组件的重渲染;同时因为key稳定,子组件不会被销毁,内部状态得以保留。

方案二:阻止父组件不必要的重渲染(可选)

如果想进一步减少父组件的重渲染次数,可以把输入表单抽成独立组件,让父组件只在categories变化时重渲染。

1. 拆分输入表单组件

把输入和提交逻辑单独封装,通过回调向父组件传递新分类:

const CategoryForm = ({ onAddCategory }) => {
    const [catName, setcatName] = useState('')
    const [catWeight, setcatWeight] = useState('')

    const handleSubmit = (e) => {
        e.preventDefault()
        let allGood = catWeight >= 1 && catWeight <= 100
        if(allGood) {
            onAddCategory({ catName, catWeight })
            // 提交后清空输入框
            setcatName('')
            setcatWeight('')
        } else alert("Something went wrong!")
    }

    return (
        <form onSubmit={handleSubmit}>
            <label>
                Enter Category Name:
                <input
                    required
                    autoCapitalize="words"
                    autoComplete="off"
                    type="text"
                    name="cat-name"
                    value={catName}
                    onChange={(e) => setcatName(e.target.value)}
                />
                <br/>
                Enter Category Weight:
                <input
                    required
                    autoComplete="off"
                    type="number"
                    name="cat-weight"
                    value={catWeight}
                    onChange={(e) => setcatWeight(e.target.value)}
                />
            </label>
            <input type="submit" value="Submit"/>
        </form>
    )
}

2. 父组件只管理分类列表状态

const ParentComponent = () => {
    const [categories, setCategories] = useState([]);

    const handleAddCategory = (newCat) => {
        setCategories([{ id: uuid(), ...newCat }, ...categories])
    }

    return (
        <div>
            <CategoryForm onAddCategory={handleAddCategory}/>
            <div id="display-categories">
                {categories.map(({ id, catName, catWeight }) => (
                    <Category key={id} name={catName} weight={catWeight}/>
                ))}
            </div>
        </div>
    );
}

这样输入框的状态变化只会触发CategoryForm的重渲染,父组件只有在categories更新时才会重渲染,进一步优化性能。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:40:27