React 中通过ref实现组件间传值
React 中的 forwardRef 的理解及用法
语法
React.forwardRef(render);
上面的代码中,forwardRef 函数接收一个名为 render 的函数(也可以将 render 方法理解成一个函数组件),返回值是 react 组件;
const render = (props, ref) => {
return <></>;
}
上面的代码中,render 函数接收 2 个参数,第一个参数为 props(父组件传递的参数对象),第二个参数为 ref (React.createRef());
综上所述,就组合成了我们就常见到的写法:
const Button = React.forwardRef((props, ref) {
return <></>;
});
一个完整的简单例子
import React from "react";
const App = (props) => {
const btnRef = React.createRef(); // 1
React.useEffect(() => {
console.log(btnRef); // 5
}, [btnRef]);
return (
<Button ref={btnRef}>一个按钮</Button> // 2
);
}
const Button = React.forwardRef((props, ref) => { // 3
return (
<button
ref={ref} // 4
>
{props.children}
</button>
);
});
export default App;
上面代码中,实现了在 App 组件中,返回了一个 Button 组件;以下是详细步骤:
- 创建了一个 ref;
- 将第一步创建的 ref 挂载到 Button 组件上;
- 将第二步传入 Button 组件的 ref 作为参数传递进来;
- 将 ref 挂载到 button DOM 节点上;
- 画面渲染完成后,控制台打印 btnRef;
总结
值得注意的是:
- 当 ref 属性用于 HTML 元素时,接收底层 DOM 元素作为其 current 属性;
- 当 ref 属性用于自定义 class 组件时,ref 接收组件的挂载实例作为其 current 属性;
- 不能在函数组件上使用 ref 属性,因为他们没有实例;
此时再来理解一下官方的定义:
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。
React hooks 中ref、useRef和forwardRef 的用法(父子组件通讯)
父调子组件 DOM 聚焦
import React, { useRef, forwardRef } from 'react';
import { Card, Button } from 'antd';
const Child = forwardRef((props,cRef) => {
// const inputRef = useRef()
const onClick = () => {
cRef.current.focus()
}
return (
<div style={{border: '1px solid red'}}>
<input type='text' ref={cRef}></input>
<Button onClick={onClick}>子组件DOM聚焦</Button>
</div>
)
})
const App: React.FC = () => {
const cRef:any = useRef() // 或 const cRef:any = createRef()
const onClick = () => {
if(cRef.current){
cRef.current.focus()
}
}
return (
<Card>
<Child ref={cRef}></Child>
<br/>
<Button onClick={onClick}>父调子组件DOM 聚焦</Button>
</Card>
)
}
export default App
父调子组件 Func 聚焦
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
import { Card, Button } from 'antd';
const Child = forwardRef((props,cRef) => {
const inputRef = useRef()
const onClick = () => {
inputRef.current.focus()
}
useImperativeHandle(cRef, () => ({
chidFunc: () => {
console.log('这是子组件函数')
inputRef.current.focus();
}
}));
return (
<div style={{border: '1px solid red'}}>
<input type='text' ref={inputRef}></input>
<Button onClick={onClick}>子组件DOM聚焦</Button>
</div>
)
})
const App: React.FC = () => {
const cRef:any = useRef() // 或 const cRef:any = createRef()
const onClickChildren = () => {
if(cRef.current){
cRef.current.chidFunc()
}
}
return (
<Card>
<Child ref={cRef}></Child>
<br/>
<Button onClick={onClickChildren}>父调子组件Func 聚焦</Button>
</Card>
)
}
export default App
注:编写中发现同时操作DOM、Func有冲突,会报错,暂未解决。
官方useImperativeHandle示例:
import React, { useRef, useImperativeHandle } from 'react';
import ReactDOM from 'react-dom';
const FancyInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} type="text" />
});
const App = props => {
const fancyInputRef = useRef();
return (
<div>
<FancyInput ref={fancyInputRef} />
<button
onClick={() => fancyInputRef.current.focus()}
>父组件调用子组件的 focus</button>
</div>
)
}
ReactDOM.render(<App />, root);
其他
import React, { forwardRef, useImperativeHandle, useEffect, useRef } from 'react'
const TestRef = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
open() {
console.log("open")
}
}))
})
function App () {
const ref = useRef()
useEffect(() => {
ref.current.open()
},[])
return(
<>
<div>石小阳</div>
<TestRef ref={ref}></TestRef>
</>
)
}
export default App