React 父组件如何调用子组件方法
子组件是类组件,使用 Refs
使用 React.createRef() 创建 Refs, Refs 可以通过 ref 属性附加到元素节点,我们就可以通过 ref.current 访问被附加的元素节点。
例如:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <input ref={this.myRef} />;
}
}
在上面的例子中,我们可以通过 myRef.current 访问到 input 节点,从而可以调用input上诸如 focus 等方法。
不同的元素类型会导致 ref 的值不同:
① 当 ref 附加的元素是 HTML 元素时,ref.current 指向的是底层的DOM元素。
② 当 ref 附加的元素是 class 组件时,ref.current 指向 class 组件实例。
注:默认情况下,不能在函数组上使用 ref 属性, 因为他们没有实例。但结合 forwardRef 方法我们就可以在函数组件上使用 ref,后面会介绍。
子组件是函数组件,使用 Refs 转发
① 使用 forwardRef 转发 Refs 到 DOM 组件,就是将 父组件的 ref 向下传递给子组件。
例如:
function Farther = ()= {
const ref = useRef(null);
return <ChildB ref={ref} />
};
const ChildB = React.forwardRef(ChildA);
function ChildA = (pops,ref) => {
return <input ref={ref} />
};
上面的例子中,父组件 Father 就能通过 ref.current 访问子组件 ChildA 中的 input 元素。
注:ChildA 子组件的第二个参数只在使用 React.createRef 定义时存在,常规的函数组件和 class 组件都是不接受 ref 参数的,而且 Props 中也不存在 ref 。转发的 ref 也只能附加的 HTML 元素 或 class 组件实例
② forwardRef 结合 useImperativeHandle 使用,自定义暴露给父组件的实例值。
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。
例如:
function Farther = ()= {
const ref = useRef(null);
return <ChildB ref={ref} />
};
const ChildB = React.forwardRef(ChildA);
function ChildA = (pops,ref) => {
useImperativeHandle(ref,()=({
name: "ChildA"
log: log,
}));
const log = ()=>{
console.log("子组件内部log")
}
return <input />
};
上面的例子中,父组件 Father 拿到的 ref.current 的值就是对象 {name:"ChildA",log:log}, 父组件也能通过 ref.current.log 调用子组件的 log 方法。
③ 在高阶组件(HOC)中转发 Refs
高阶组件是参数为组件,返回值为新组件的函数。
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。
我们可以在 HOC 中使用自定义的 prop 属性接收 ref 并传递给 子组件.
例如:
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}