React 父子组件互相调用方法和变量值

359 阅读3分钟

父子组件传递方法或值时,子组件负责暴露和接收,父组件负责调用和传递。因此,接触到最多的就是 refuseImperativeHandle()props

总结公式

  • 子调父值
    • 子收变量名={父变量名}
    • 子组件以子收变量名={父变量名}定义属性,传值给子组件。子组件以 props 来接收,使用props.子收变量名获取变量值。
  • 子调父方法
    • 子收方法名={父方法名}
    • 子组件以子收方法名={父方法名}定义属性,传值给子组件。子组件以 props 来接收,使用props.子收方法名()调用方法。
  • 父调子值
    • useImperativeHandle(ref,()=>({变量}))
    • 子组件使用 useImperativeHandle(ref,()=>({变量}))暴露变量。父组件在子组件处定义ref={xxRef}, 使用xxRef.current.变量获取变量值。
  • 父调子方法
    • useImperativeHandle(ref,()=>({方法名}))
    • 子组件使用useImperativeHandle(ref,()=>({方法名}))暴露方法。父组件在子组件处定义ref={xxRef}, 使用xxRef.current.方法名()调用方法。

接下来开始实践吧~

父组件调用子组件的值

需要在同一个文件夹下创建 ParentCom.jsxSonCom.jsx 两个文件,分别代表父组件和子组件。

父组件

import SonCom from './SonCom.jsx';
import React from 'react';

const ParentCom = () => {
    const ref = React.useRef(null);
    const [saveData, setSaveData] = React.useState(null); // 用来存储子组件的值

    return (
        <div style={{ border: '2px solid #7c7c', padding: 10, width: '580px', height: '200px' }}>
            <span style={{ fontWeight: 'bold' }}>{`Parent ===> 绿色框内是父组件`}</span>
            <br />
            <div>
                <span>来自子组件:</span>
                {saveData}
                <button
                    onClick={() => {
                        const dataFormSon = ref.current?.sonData?.name; // 调用子组件的值
                        setSaveData(dataFormSon); // 存储并展示
                    }}
                >
                    获取数据
                </button>
                <button
                    onClick={() => {
                        setSaveData(null);
                    }}
                >
                    清空
                </button>
            </div>
            <SonCom ref={ref} />
        </div>
    );
};

export default ParentCom;

子组件

通过使用 useImperativeHandle() 暴露数据

import React from 'react';

const SonCom = React.forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({ sonData }), []); // 暴露给父组件
    const sonData = { name: 'here is from son.' }; // 子组件的值
   
    return (
        <div style={{ border: '2px solid #7c7ced', padding: 10, width: '480px', height: '100px', marginTop: 10 }}>
            <span style={{ fontWeight: 'bold' }}>{`Son ===> 紫色框内是子组件`}</span>
            <br />
        </div>
    );
});


export default SonCom;

子组件调用父组件的值

需要在同一个文件夹下创建 ParentCom.jsxSonCom.jsx 两个文件,分别代表父组件和子组件。

父组件

只需要在子组件上将需要传的值(或方法)作为属性传递即可。

import SonCom from './SonCom.jsx';
import React from 'react';

const ParentCom = () => {
    const parentData = { name: 'here is from parent.' };

    return (
        <div style={{ border: '2px solid #7c7c', padding: 10, width: '580px', height: '200px' }}>
            <span style={{ fontWeight: 'bold' }}>{`Parent ===> 绿色框内是父组件`}</span>
            <br />
            <SonCom parentData={parentData} />
        </div>
    );
};

export default ParentCom;

子组件

import React from 'react';

const SonCom = React.forwardRef((props, ref) => {
    const {  parentData } = props;

    return (
        <div style={{ border: '2px solid #7c7ced', padding: 10, width: '480px', height: '100px', marginTop: 10 }}>
            <span style={{ fontWeight: 'bold' }}>{`Son ===> 紫色框内是子组件`}</span>
            <br />
            <div>
                <span>来自父组件:</span>
                {parentData?.name}
            </div>
        </div>
    );
});

export default SonCom;

父组件调用子组件方法

需要在同一个文件夹下创建 ParentCom.jsxSonCom.jsx 两个文件,分别代表父组件和子组件。

父组件

import SonCom from './SonCom.jsx';
import React from 'react';

const ParentCom = () => {
    const ref = React.useRef(null);

    return (
        <div style={{ border: '2px solid #7c7c', padding: 10, width: '580px', height: '200px' }}>
            <span style={{ fontWeight: 'bold' }}>{`Parent ===> 绿色框内是父组件`}</span>
            <br />
            <button
                onClick={() => {
                    ref.current?.handleClick('你好,我是父组件'); // 调用子组件的方法
                }}
            >
                点击子组件方法
            </button>
            <SonCom ref={ref}/>
        </div>
    );
};

export default ParentCom;

子组件

通过使用 useImperativeHandle() 暴露数据。

import React from 'react';

const SonCom = React.forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({ handleClick }), []); // 暴露给父组件
    
    //  子组件的方法
    const handleClick = (text) => {
        alert(`子级组件方法: ${text}`);
    };

    return (
        <div style={{ border: '2px solid #7c7ced', padding: 10, width: '480px', height: '100px', marginTop: 10 }}>
            <span style={{ fontWeight: 'bold' }}>{`Son ===> 紫色框内是子组件`}</span>
            <br />
        </div>
    );
});


export default SonCom;

子组件调用父组件方法

需要在同一个文件夹下创建 ParentCom.jsxSonCom.jsx 两个文件,分别代表父组件和子组件。

父组件

只需要在子组件上将需要传的值(或方法)作为属性传递即可。

import SonCom from './SonCom.jsx';
import React from 'react';

const ParentCom = () => {
    // 父组件的方法
    const handleClick = (text) => {
        alert(`父级组件方法: ${text}`);
    };

    return (
        <div style={{ border: '2px solid #7c7c', padding: 10, width: '580px', height: '200px' }}>
            <span style={{ fontWeight: 'bold' }}>{`Parent ===> 绿色框内是父组件`}</span>
            <br />
            <SonCom handleParentClick={handleClick} />
        </div>
    );
};

export default ParentCom;

子组件

import React from 'react';

const SonCom = React.forwardRef((props, ref) => {
    const { handleParentClick } = props; // 接收来自父组件的方法

    return (
        <div style={{ border: '2px solid #7c7ced', padding: 10, width: '480px', height: '100px', marginTop: 10 }}>
            <span style={{ fontWeight: 'bold' }}>{`Son ===> 紫色框内是子组件`}</span>
            <br />
            <button onClick={() => handleParentClick('你好,我是子组件')}>点击父级组件</button>
        </div>
    );
});

export default SonCom;

代码整合

以下是将父子组件代码全部整合到一起的效果,只在一个文件下完成(以.tsx为例),可以试试看效果~

注:若是.jsx的,只需要把类型相关的代码去掉即可

import React, { useImperativeHandle, useState } from 'react';

type SonProps = {
    handleParentClick: (text?: string) => void;
    parentData?: string;
};

/**
 * 子组件
 * @returns
 */
const SonCom = React.forwardRef((props: SonProps, ref) => {
    useImperativeHandle(ref, () => ({ handleSonClick, sonData }), []); // 暴露给父组件的方法和值
    const sonData = { name: 'here is from son.' }; // // 子组件内部值
    const [saveData, setSaveData] = useState(null);

    const { handleParentClick, parentData } = props; // 接收来自父组件的方法和值

    // 子组件内部方法
    const handleSonClick = (text?: string) => {
        // 一些逻辑
        alert(`子级组件方法: ${text}`);
    };

    return (
        <div style={{ border: '2px solid #7c7ced', padding: 10, width: '480px', height: '100px', marginTop: 10 }}>
            <span style={{ fontWeight: 'bold' }}>{`Son ===> 紫色框内是子组件`}</span>
            <br />
            <button onClick={() => handleParentClick('你好,我是子组件')}>点击父级组件</button>
            <div>
                <span>来自父组件:</span>
                {saveData}
                <button
                    onClick={() => {
                        setSaveData(parentData?.name);
                    }}
                >
                    获取数据
                </button>
                <button
                    onClick={() => {
                        setSaveData(null);
                    }}
                >
                    清空
                </button>
            </div>
        </div>
    );
});

/**
 * 父组件
 * @returns
 */
const ParentCom = () => {
    const ref = React.useRef(null);
    const parentData = { name: 'here is from parent.' };
    const [saveData, setSaveData] = useState(null);

    const handleParentClick = (text?: string) => {
        // 一些逻辑
        alert(`父级组件方法: ${text}`);
    };

    return (
        <div style={{ border: '2px solid #7c7c', padding: 10, width: '580px', height: '200px' }}>
            <span style={{ fontWeight: 'bold' }}>{`Parent ===> 绿色框内是父组件`}</span>
            <br />
            <button
                onClick={() => {
                    ref.current?.handleSonClick('你好,我是父组件');
                }}
            >
                点击子组件方法
            </button>
            <div>
                <span>来自子组件:</span>
                {saveData}
                <button
                    onClick={() => {
                        setSaveData(ref.current?.sonData?.name);
                    }}
                >
                    获取数据
                </button>
                <button
                    onClick={() => {
                        setSaveData(null);
                    }}
                >
                    清空
                </button>
            </div>
            <SonCom ref={ref} handleParentClick={handleParentClick} parentData={parentData} />
        </div>
    );
};

export default ParentCom;