【问题标题】:Array of child component not being re-rendered if param in parent changes如果父项中的参数发生更改,则子组件的数组不会被重新渲染
【发布时间】:2020-05-14 12:55:54
【问题描述】:

我是 React 新手。我必须实现一个按钮,每次单击按钮时都会在页面上添加几个字段(文本框、选择、复选框)。我通过参考示例http://jsfiddle.net/rpd32h1g/ 实现了这一点。 我有 3 节课 1. 父类,我在其中编写了填充 subSectionList 的逻辑,它定义了单击按钮时需要添加的组件/字段。 2. 子组件(AddAccount.js),它从父组件接收参数 subSectionList 并将其传递给子子 Account.js 的数组。该数组是在单击按钮时创建的,每次单击时数组都会更新。 3. 子子组件(Account.js),它接收参数subSectionList 并对其进行迭代以显示所需的组件。 问题:如果 subSectionList 的值在父项中更改,则子子 Account.js 不会被重新渲染。 下面是代码供参考:

调用下面的函数(findApplicableFields)onChange使用按钮添加的字段(该方法存在于父类中)。

const findApplicableFields = (questions, questionName, optionName, currentQuestion) => {
 const newSubSectionList = questions.map(question => {
            if (question.name === questionName) {
                return { ...question, children };
            }
            return { ...question };
        });
 subSectionList[questionName] = newSubSectionList;
}

下面是孩子的代码sn-p:

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import Account from './Account';

export default function AddAccount({
    buttonText,
    type = 'button',
    buttonClass,
    questions,
    checkoutJourney,
    handleBlur,
    isValid,
    question,
    onChange,
    subSectionList,
    ...rest
}) {
    const [addAccount, setAddAccount] = useState(true);
    const [accountArray, setAccountArray] = useState(
        checkoutJourney['addAccountArray'] == null ? [] : checkoutJourney['addAccountArray']
    );
    let i = 0;

    const _onClick = e => {
        e.preventDefault();
        setAddAccount(true);
        setAccountArray([
            <Account
                key={i}
                questions={questions}
                checkoutJourney={checkoutJourney}
                handleBlur={handleBlur}
                onChange={onChange}
                isValid={isValid}
                questionName={question.name}
                subSectionList={subSectionList}
                {...rest}
            />,
            ...accountArray
        ]);
        checkoutJourney['addAccountArray'] = accountArray;
        checkoutJourney['subSectionList'] = subSectionList;
        i++;
    };
    return (
        <>
            {addAccount ? accountArray : null}
            <div className={buttonClass}>
                <button
                    className="button button--dark button--primary"
                    type={type}
                    onClick={e => {
                        _onClick(e);
                    }}
                >
                    {buttonText}
                </button>
            </div>
        </>
    );
}

AddAccount.propTypes = {
    _onClick: PropTypes.func,
    type: PropTypes.string,
    buttonText: PropTypes.string,
    buttonClass: PropTypes.string,
    questions: PropTypes.array,
    onChange: PropTypes.func,
    checkoutJourney: PropTypes.object,
    rest: PropTypes.object,
    handleBlur: PropTypes.func,
    isValid: PropTypes.object,
    question: PropTypes.any,
    subSectionList: PropTypes.object
};

AddAccount.defaultProps = {
    buttonText: '+ Add another account'
};

下面是需要重新渲染的sub child的sn-p代码:

import React from 'react';
import PropTypes from 'prop-types';
import TextInput from '../TextInput/WS2TextInput';
import Select from '../Select/WS2Select';
import AddressMoreServices from '../AddMoreServices/AddMoreServices';
import Checkbox from '../Checkbox/Checkbox';
const Account = ({ checkoutJourney, handleBlur, isValid, onChange, subSectionList, questionName, ...rest }) => {

    return (
        <>
            {subSectionList[questionName].map((question, i, questions) => {
               //The logic to render the field based on type of question 
 })}
        </>
    );
}

Account.propTypes = {
    questions: PropTypes.array,
    onChange: PropTypes.func,
    checkoutJourney: PropTypes.object,
    rest: PropTypes.object,
    handleBlur: PropTypes.func,
    isValid: PropTypes.object,
    subSectionList: PropTypes.object,
    questionName: PropTypes.string
};

export default Account;


【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    发生这种情况是因为您没有更改 subSectionList 变量,而是操作其内容(在对象中添加和删除属性)。 只有当它们绑定的变量完全改变(换句话说,它们在内存中交换)时,React 才会重新渲染 Components。 React 通过在新旧状态变量之间执行shallow comparison 来识别更改。在您的情况下,变量 subSectionList 始终相同(因为您只是向其添加属性),因此 React 不会重新渲染 &lt;AddAccount /&gt;&lt;Account /&gt;

    为了重新渲染 &lt;AddAccount /&gt;&lt;Account /&gt;,您必须通过在父组件中调用 setState 来重新创建整个 subSectionList

    const [subSectionList, setSubSectionList] = useState([]);
    
    setSubSectionList([
        ...subSectionList,
        [questionName]: newSubSectionList
    ]);
    

    这将在内存中生成一个全新的subSectionList 数组,使 React 重新渲染所有依赖它的组件。

    【讨论】:

    • 在我的例子中, subsectionList 中的键将保持不变,只有键的值会改变。您能否帮助了解在这种情况下如何重新渲染孩子?
    • @SomyaSrivastava 你仍然需要生成一个全新的变量,就像上面的例子一样。只有当它依赖的变量在内存中完全交换时,React 才会重新渲染组件。仅仅改变你的对象值是不够的。
    • 我已经把小节列表设置好了。但是,子子项(Account.js)仍然没有被重新渲染,而更新的 subSectionList 正在子项中打印,即 AddAccount.js let newSubSectionList = {}; subChildren.map(child =&gt; { if (child.apply_to === optionName) { newSubSectionList[child.name] = true; } else { newSubSectionList[child.name] = false; } }); setSubSectionList(newSubSectionList);
    • 我找到了原因,问题是由于Account.js的数组而发生的。如果我删除数组并仅使用在单击按钮时设置为 true 的标志呈现 Account.js,那么我的子子将在 subSecTionList 更改时重新呈现。但是这种方法的问题是我无法在单击添加帐户按钮时添加多个帐户对象。你能帮我解决这个问题吗?
    • 恐怕我无法在评论部分提供更多帮助,因为它不开放用于扩展讨论。如果Components 绑定到正在更改的状态变量,则应始终重新渲染。我认为您应该仔细查看您的代码,并确保所有组件都正确绑定到您的状态。
    猜你喜欢
    • 2020-01-20
    • 2022-08-10
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 2019-12-12
    • 2018-08-12
    相关资源
    最近更新 更多