React Hooks父组件中获取子组件实例值

14,845 阅读2分钟

通过useImperativeHandle将子组件中值暴露给父组件

一、获取子组件 formFields

父组件

import React, { useRef } from "react";
import { Modal } from "antd";
import SubForm from "./subForm";

const Parent = () => {
  const getFormValue = useRef();

  const handleOk = () => {
    const fields = getFormValue.current.formFields;
    console.log(fields);
  };

  return (
    <Modal onOk={handleOk}>
      <SubForm ref={getFormValue} />
    </Modal>
  );
};

export default Parent;

子组件

import React, { useImperativeHandle, useRef, forwardRef } from "react";
import { Form } from "antd";

const SubForm = (props, ref) => {
  const [form] = Form.useForm();
  const formRef = useRef();

  useImperativeHandle(ref, () => ({
    formFields: form.getFieldsValue(),
  }));

  return (
    <Form form={form} ref={formRef}>
      ...
    </Form>
  );
};

const WrappedForm = forwardRef(SubForm);
export default WrappedForm;

按上面子组件的写法会存在获取到 form 是初始值,如果我们修改了 form 里 Item 的值,会获取不到,还有的场景需要我们先进行必填表单校验,直接获取子组件 form 实例值,会跳过表单校验,这样也会出现问题

解决思路:子组件暴露出 form,父组件先进行表单校验,再获取 form 值

修改后父组件

import React, { useRef } from "react";
import { Modal } from "antd";
import SubForm from "./subForm";

const Parent = () => {
  const getFormValue = useRef();

  const handleOk = () => {
    const { getFieldsForm }: any = getFormValue.current;
    // 首先表单校验
    getFieldsForm.validateFields().then(() => {
      const values = getFieldsForm.getFieldsValue();
      console.log(values);
    });
  };

  return (
    <Modal onOk={handleOk}>
      <SubForm ref={getFormValue} />
    </Modal>
  );
};

export default Parent;

修改后子组件

import React, { useImperativeHandle, useRef, forwardRef } from "react";
import { Form } from "antd";

const SubForm = (props, ref) => {
  const [form] = Form.useForm();

  useImperativeHandle(ref, () => ({
    formFields: form,
  }));

  return <Form form={form}>...</Form>;
};

const WrappedForm = forwardRef(SubForm);
export default WrappedForm;

二、获取子组件实例值(useState 内容)

父组件

import React, { useRef } from "react";
import { Button } from "antd";
import { ExportOutlined } from "@ant-design/icons";
import ChildComponent from "./component/ChildComponent";

const ParentCom = () => {
  const getTypeValues = useRef();

  // 导出
  const exportHandle = () => {
    // 获取子组件实例值
    const { typeFields }: any = getTypeValues.current;
    const type = typeFields.statType;

    const ifr = document.createElement("iframe");
    ifr.style.display = "none";
    ifr.src = `/aaa/bbb/export?type=${type}`;
    document.body.appendChild(ifr);
  };

  return (
    <>
      <ChildComponent ref={getTypeValues} />
      <Button type="primary" icon={<ExportOutlined />} onClick={exportHandle}>
        导出
      </Button>
    </>
  );
};
export default ParentCom;

子组件

import React, { useState, useImperativeHandle, forwardRef } from "react";

type StatTypeProps = 1 | 2 | 3;

const ChildComponent = (ref: any) => {
  const [statType, setStatType] = useState < StatTypeProps > 1;

  useImperativeHandle(ref, () => ({
    typeFields: {
      statType,
    },
  }));

  return (
    <Select value={statType} onChange={(val) => setStatType(val)}>
      {statTypeOpts.map((item) => (
        <Option value={item.value} key={item.value}>
          {item.label}
        </Option>
      ))}
    </Select>
  );
};
export default forwardRef(ChildComponent);

结语

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle应当与 forwardRef 一起使用.