【问题标题】:Reactjs: How to access state of child component.from a method in parent component that depends on state of child componentReactjs:如何访问子组件的状态。从依赖于子组件状态的父组件中的方法
【发布时间】:2021-07-06 07:53:09
【问题描述】:

我需要访问父组件中定义的方法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 进行更改后:

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

【问题讨论】:

  • 为什么handleCancelEdit 不能只存在于子组件中?
  • 你是否试图将状态从孩子传递给父母?如果是这样,您需要将对父级中的函数的引用作为道具传递给子级。然后,孩子可以根据需要调用该函数,并且可以将自己与其他子组件区分开来。另一种选择是使用像 redux 这样的状态管理库。
  • @Denno 我想我需要使用ref,就像我在其他问题中看到的那样,但我不知道如何使用它。
  • @Ezra 我是不是在做同样的事情,即 在我的代码中将父级中的引用作为道具传递给子级?如果没有,我该怎么做?
  • 父级在哪里调用handleCancelEdit?它是试图取消所有孩子还是只取消特定孩子?

标签: javascript reactjs react-hooks react-component


【解决方案1】:

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

在ChildComponent 中,我们将isEdit 和setEdit 移动到props。 handleCancelEdit就是() =&gt; 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>
  );
}

【讨论】:

  • "(是否也需要清除onChange设置的状态)"?是的,当单击“取消”时,所做的任何编辑都必须恢复为上次保存的文本。
  • 这是不是forwardRef 的首选方法,因为forwardRef 用于后面的场合?
  • 是的,这是首选方法。我经常在Lifting State Up 上链接到一个文档部分
  • 嘿,我已经实现了你提到的父母和孩子。当我单击编辑时,该值将更改为文本输入,但不可编辑。 onChange 在某处不工作!
  • 我的第一个想法是将所有初始/保存的值存储在父级中的单独状态中。但现在我认为孩子应该存储文本输入状态,它应该在点击“保存”而不是每次更改时更新父。它有点取决于所需的行为。已经“保存”后可以“取消”以恢复到以前的值吗?是整个表格一个“保存”/“取消”还是每个字段一个?我想我明白了:codesandbox.io/s/react-cancelable-edit-lq4p4?file=/src/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
  • 2018-04-20
  • 2018-07-17
  • 1970-01-01
  • 2018-09-08
  • 2015-04-15
  • 1970-01-01
相关资源
最近更新 更多