如何在 React 中访问孩子的状态用React.createRef() 回调引用更多信息

2022-08-30 00:07:38

我有以下结构:

FormEditor- 保存FieldEditor的多个实例 - 编辑表单的字段并在其状态下保存有关它的各种值FieldEditor

当在FormEditor中单击按钮时,我希望能够从所有组件中收集有关字段的信息,处于其状态的信息,并将其全部放在FormEditor中。FieldEditor

我考虑过将有关 字段的信息存储在 的状态之外,并将其置于 的状态中。但是,这需要在更改其每个组件时侦听它们的信息,并将其信息存储在其状态中。FieldEditorFormEditorFormEditorFieldEditor

我不能只访问儿童状态吗?它理想吗?


答案 1

在我详细介绍如何访问子组件的状态之前,请务必阅读 Markus-ipse 关于处理此特定方案的更好解决方案的答案。

如果确实希望访问组件子级的状态,则可以为每个子级分配一个调用的属性。现在有两种方法可以实现引用:使用和回调引用。refReact.createRef()

React.createRef()

这是目前推荐使用 React 16.3 引用的方法(有关更多信息,请参阅文档)。如果您使用的是早期版本,请参阅下面有关回调引用的信息。

您需要在父组件的构造函数中创建新引用,然后通过该属性将其分配给子组件。ref

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.FieldEditor1 = React.createRef();
  }
  render() {
    return <FieldEditor ref={this.FieldEditor1} />;
  }
}

为了访问这种引用,您需要使用:

const currentFieldEditor1 = this.FieldEditor1.current;

这将返回已装载组件的实例,以便您可以使用 来访问状态。currentFieldEditor1.state

只需快速说明一下,如果您在 DOM 节点而不是组件(例如 )上使用这些引用,则将返回底层 DOM 元素而不是组件实例。<div ref={this.divRef} />this.divRef.current

回调引用

此属性采用一个回调函数,该函数传递对附加组件的引用。此回调在组件挂载或卸载后立即执行。

例如:

<FieldEditor
    ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
    {...props}
/>

在这些示例中,引用存储在父组件上。若要在代码中调用此组件,可以使用:

this.fieldEditor1

然后用于获取状态。this.fieldEditor1.state

需要注意的一点是,在尝试访问子组件之前,请确保该^_^组件已呈现

如上所述,如果您在 DOM 节点而不是组件(例如 )上使用这些引用,则将返回底层 DOM 元素而不是组件实例。<div ref={(divRef) => {this.myDiv = divRef;}} />this.divRef

更多信息

如果你想阅读更多关于 React 的 ref 属性的信息,请查看 Facebook 的这个页面

确保你阅读了“不要过度使用引用”部分,该部分说你不应该使用孩子的“让事情发生”。state


答案 2

如果您已经为各个 FieldEditors 提供了一个 onChange 处理程序,我不明白为什么您不能将状态向上移动到 FormEditor 组件,然后从那里向下传递一个回调到 FieldEditors,该回调将更新父状态。对我来说,这似乎是一种更像 React 的方式。

也许可以这样说:

const FieldEditor = ({ value, onChange, id }) => {
  const handleChange = event => {
    const text = event.target.value;
    onChange(id, text);
  };

  return (
    <div className="field-editor">
      <input onChange={handleChange} value={value} />
    </div>
  );
};

const FormEditor = props => {
  const [values, setValues] = useState({});
  const handleFieldChange = (fieldId, value) => {
    setValues({ ...values, [fieldId]: value });
  };

  const fields = props.fields.map(field => (
    <FieldEditor
      key={field}
      id={field}
      onChange={handleFieldChange}
      value={values[field]}
    />
  ));

  return (
    <div>
      {fields}
      <pre>{JSON.stringify(values, null, 2)}</pre>
    </div>
  );
};

// To add the ability to dynamically add/remove fields, keep the list in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

原始 - 预挂钩版本:

class FieldEditor extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    const text = event.target.value;
    this.props.onChange(this.props.id, text);
  }

  render() {
    return (
      <div className="field-editor">
        <input onChange={this.handleChange} value={this.props.value} />
      </div>
    );
  }
}

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};

    this.handleFieldChange = this.handleFieldChange.bind(this);
  }

  handleFieldChange(fieldId, value) {
    this.setState({ [fieldId]: value });
  }

  render() {
    const fields = this.props.fields.map(field => (
      <FieldEditor
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        {fields}
        <div>{JSON.stringify(this.state)}</div>
      </div>
    );
  }
}

// Convert to a class component and add the ability to dynamically add/remove fields by having it in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

ReactDOM.render(<App />, document.body);