Reactjs:如何从依赖于子组件状态的父组件中的方法访问子组件的状态

时间:2021-04-10 18:35:22

标签: javascript reactjs react-hooks react-component

我需要访问父组件中定义的方法 handleCancelEdit()。但是,这里的问题是每个子组件都有自己的 cancelEdit 状态。现在,发生的事情是,如果我从一个子组件调用 handleCancelEdit(),那么所有其他相同的子组件都会获取状态并更新自己(该方法尚未完全定义)。这就是为什么,我在子组件中定义了cancelEdit状态,认为它只属于这个子组件。

现在,我如何让 handleCancelEdit() 方法更改调用它的唯一子组件?

家长:

function Parent() {
    const handleCancelEdit = () => {
        setCancelEdit(!cancelEdit);  // defined in child component
        setEdit(!edit);       // defined in child component
        ...
    };
    return (
    <div>
        <ChildComponent
            fieldName={"Email"}
            value={email}
            inputType={"text"}
            placeHolder={"Enter email"}
            name={"email"}
            on_change={(e)=>setEmail(e.target.value)}
            on_click={handleUserEmail}
         />
         <ChildComponent
             fieldName={"About"}
             value={about}
             inputType={"text"}
             placeHolder={"Enter some details about yourself"}
             name={"about"}
             on_change={(e)=>setAbout(e.target.value)}
             on_click={handleUserAbout}
         />
    </div>
    );
}

子组件:

function ChildComponent({fieldName, value, inputType, placeHolder, name, on_change, on_click}) {
    const [ edit, setEdit ] = useState(false);
    const [ cancelEdit, setCancelEdit ] = useState(false)
    const isEdit = edit;
    return (
        <p>{fieldName}: {value === ''? (
            <span>
                <input type={inputType} placeholder={placeHolder}
                    name={name}  onChange={on_change}
                />
                <button type="submit" onClick={on_click}>Add</button>
            </span>
            ) : ( !isEdit ? (<span> {value} <button onClick={e=>setEdit(!edit)}>Edit</button></span>) :
            (<span>
                <input type={inputType} value={value}
                        name={name}  onChange={on_change}
                />
                <button type="submit" onClick={on_click}>Save</button>
                <button type="submit" onClick={handleCancelEdit}>Cancel</button>
            </span>)                            
            )}
        </p>
    );
};

我希望它能让一个子组件不应该让其他组件更新是可以理解的。现在,在这种情况下我该怎么做?

编辑

根据 Linda Paiste 进行更改后: gif of error

即使父组件和子组件中的 onChange 都正确,子组件中的输入字段不起作用!

1 个答案:

答案 0 :(得分:1)

向下传递状态和数据总是比向上传递更合乎逻辑。如果 Parent 需要与 edit 状态交互,那么该状态应该存在于父状态中。当然,我们希望每个孩子都有独立的 edit 状态,所以父母不能只有一个 boolean。每个孩子都需要一个 boolean。我推荐一个由字段的 object 属性键控的 name

ChildComponent 中,我们将 isEditsetEdit 移动到 props。 handleCancelEdit 就是 () => setEdit(false)(是否还需要清除 onChange 设置的状态?)。


function ChildComponent({fieldName, value, inputType, placeHolder, name, onChange, onSubmit, isEdit, setEdit}) {
    return (
        <p>{fieldName}: {value === ''? (
            <span>
                <input type={inputType} placeholder={placeHolder}
                    name={name}  onChange={onChange}
                />
                <button type="submit" onClick={onSubmit}>Add</button>
            </span>
            ) : ( !isEdit ? (<span> {value} <button onClick={() =>setEdit(true)}>Edit</button></span>) :
            (<span>
                <input type={inputType} value={value}
                        name={name}  onChange={onChange}
                />
                <button type="submit" onClick={onSubmit}>Save</button>
                <button type="submit" onClick={() => setEdit(false)}>Cancel</button>
            </span>)                            
            )}
        </p>
    );
};

Parent 中,我们需要存储这些 isEdit 状态,并为每个字段创建一个 setEdit 函数。

function Parent() {
  const [isEditFields, setIsEditFields] = useState({});

  const handleSetEdit = (name, isEdit) => {
    setIsEditFields((prev) => ({
      ...prev,
      [name]: isEdit
    }));
  };

  /* ... */

  return (
    <div>
      <ChildComponent
        fieldName={"Email"}
        value={email}
        inputType={"text"}
        placeHolder={"Enter email"}
        name={"email"}
        onChange={(e) => setEmail(e.target.value)}
        onSubmit={handleUserEmail}
        isEdit={isEditFields.email}
        setEdit={(isEdit) => handleSetEdit("email", isEdit)}
      />
      <ChildComponent
        fieldName={"About"}
        value={about}
        inputType={"text"}
        placeHolder={"Enter some details about yourself"}
        name={"about"}
        onChange={(e) => setAbout(e.target.value)}
        onSubmit={handleUserAbout}
        isEdit={isEditFields.about}
        setEdit={(isEdit) => handleSetEdit("about", isEdit)}
      />
    </div>
  );
}

您可以通过将 values 存储为单个状态而不是单个 useState 挂钩来清理大量重复代码。现在可以从 name 自动生成 5 个道具。

function Parent() {
  const [isEditFields, setIsEditFields] = useState({});

  const [values, setValues] = useState({
      about: '',
      email: ''
  });

  const getProps = (name) => ({
      name,
      value: values[name],
      onChange: (e) => setValues(prev => ({
          ...prev,
          [name]: e.target.value
      })),
      isEdit: isEditFields[name],
      setEdit: (isEdit) => setIsEditFields(prev => ({
          ...prev,
          [name]: isEdit
      }))
  });

  const handleUserEmail = console.log // placeholder
  const handleUserAbout = console.log // placeholder

  return (
    <div>
      <ChildComponent
        fieldName={"Email"}
        inputType={"text"}
        placeHolder={"Enter email"}
        onSubmit={handleUserEmail}
        {...getProps("email")}
      />
      <ChildComponent
        fieldName={"About"}
        inputType={"text"}
        placeHolder={"Enter some details about yourself"}
        onSubmit={handleUserAbout}
        {...getProps("about")}
      />
    </div>
  );
}