react教程,forwardRef()如何使用?ref如何跨组件传递?
发布于 作者:苏南大叔 来源:程序如此灵动~本篇文章的使用场景对于一般人来说,并不常见。因为是用于自定义组件使用的,意思是提供react
的各自控件给外部调用的时候,才会用的到forwardRef
【而且还可以被普通的ref
替代】。而对于控件使用者来说,forwardRef
是完全无感的,用到的只有常规的ref
。
苏南大叔的程序如此灵动博客,这里记录苏南大叔和计算机代码的故事。本文描述react
项目的forwardRef
的使用方法。测试环境:create-react-app@5.0.1
,react@18.2.0
,react-dom@18.2.0
,node@16.14.2
。
本文主要介绍做组件库时会用到的forwardRef
函数,普通情况下是用不到的。平时使用的是ref
,请参考:
https://newsn.net/say/react-ref.html
ref
不能通过props
传递,但是【非ref
】可以
由于ref
在使用方式上,和普通的props
是一样的。
- 对于子组件来说,是否可以通过
props.ref
获得ref
引用呢?答案是不能。 - 那么,如果把
ref
改名成ref2
,是否可以获得props.ref2
呢?答案是可以。
测试用例为Sunan1
:
export class Sunan1 extends React.Component {
state = {who: "苏南大叔"};
componentDidMount() {
console.log(this.props)
}
render() {
return (<div ref={this.props.ref2}>一号子组件(类组件)</div>);
}
}
调用方式:
<Sunan1 ref={ref1} ref2={ref2} sn="SN" />
测试props
输出了ref2
和sn
这两个props
,并且ref2
成功作用于div
上,并没有输出预期的ref
属性。
ref
是个被特殊处理的关键字,ref
引用指代了子组件本身,在父组件中可以使用ref
获得子组件内部数据。比如:
<button onClick={() => { console.log(ref1.current.state.who) }}>1号组件</button>
函数组件不能直接使用ref
如果在函数式组件实例上,使用ref
,则会有报错信息。
组件定义:
const Sunan2 = (props) => {
console.log(props);
return (<div ref={props.ref}>函数组件与ref</div>);
}
组件调用:
function App() {
const ref1 = useRef(null);
return (
<div className="App">
<Sunan2 ref={ref1}>
</div>
);
}
export default App;
这个时候会有警告信息提示:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
大意是:ref
属性是无法叠加于函数式组件上的,【注意:ref
可以直接作用于类组件】。写了ref
也没用,但是可以使用React.forwardRef
把ref
属性给再加工一下。
`ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.
大意是:在组件里面不能使用props.ref
。
函数组件可以使用非ref
字样
那么,把例子中的ref
字样换成ref2
字样呢?
组件定义:
const Sunan2 = (props) => {
console.log(props);
return (<div ref={props.ref2}>函数组件与ref</div>);
}
组件调用:
function App() {
const ref2 = useRef(null);
return (
<div className="App">
<Sunan2 ref2={ref2}>
</div>
);
}
export default App;
结果一切正常,可以正常使用。这是不是比较神奇了。当然,这里在子组件里面,是使用props.ref2
获得的ref
定义。
forwardRef()
目前的基本结论是:使用ref
字样,就会被系统自动过滤掉。如果换成非ref
字样,比如ref2
字样,就可以通过props.
来获取并正常使用ref
,但是显然这并不是react
希望大家这么做的,官方推荐使用的是:forwardRef()
。
子组件定义:
export const Sunan3 = forwardRef(function (props, ref) {
console.log("forwardRef(函数组件)", props, ref);
return (
<>
<input type="text" ref={ref} defaultValue="forwardRef(函数组件)" />
</>
)
});
class Sunan4_ extends React.Component {
componentDidMount() {
console.log("forwardRef(类式组件)", this.props)
}
render() {
return (
<div>
<span ref={this.props.ref2}>forwardRef(类式组件)</span>
</div>
)
}
};
export const Sunan4 = forwardRef((props, ref) => <Sunan4_ ref2={ref} {...props} />);
组件调用:
<Sunan3 ref={ref3} />
<Sunan4 ref={ref4} />
<button onClick={() => { console.log(ref4.current.innerHTML) }}>4号组件</button>
forwardRef
相关总结
组件 | forward中间件ref | 子组件的父组件传递ref | 子组件的父组件传递ref2 |
---|---|---|---|
函数组件 | 警告,系统强制过滤 | 子组件props.ref2 | |
类式组件 | 指代子组件,可访问子组件内部 | 子组件this.props.ref2 | |
forwardRef(函数组件) | 直接传入子组件使用 | 子组件接收两个参数props,ref | 子组件props.ref2 |
forwardRef(类式组件) | 必须改名非ref | 只在forwardRef()函数内有效 | 子组件this.props.ref2 |
完整代码
App.js
:
import React, { createRef, useRef } from "react";
import { Sunan1, Sunan2, Sunan3, Sunan4 } from "./SunanLib";
function App() {
const ref1 = useRef(null);
const ref2 = useRef(null);
const ref3 = useRef(null);
const ref4 = useRef(null);
return (
<div className="App">
<div>
<Sunan1 ref={ref1} ref2={ref2} sn="SN" />
<button onClick={() => { console.log(ref1.current.state.who) }}>1号组件</button>
</div>
<div>
<Sunan2 ref={ref1} ref2={ref2} sn="SN" />
</div>
<div>
<Sunan3 ref={ref3} /></div>
<div>
<Sunan4 ref={ref4} />
<button onClick={() => { console.log(ref4.current.innerHTML) }}>4号组件</button>
</div>
</div>
);
}
export default App;
SunanLib.js
:
import React, { forwardRef } from "react";
export class Sunan1 extends React.Component {
state = {who: "苏南大叔"};
componentDidMount() {
console.log(this.props)
}
render() {
return (<div ref={this.props.ref2}>一号子组件(类组件)</div>);
}
}
export const Sunan2 = (props) => {
console.log(props);
return (<div ref={props.ref2}>二号子组件(函数组件)</div>);
}
export const Sunan3 = forwardRef(function (props, ref) {
console.log("forwardRef(函数组件)", props, ref);
return (
<>
<input type="text" ref={ref} defaultValue="forwardRef(函数组件)" />
</>
)
});
class Sunan4_ extends React.Component {
componentDidMount() {
console.log("forwardRef(类式组件)", this.props)
}
render() {
return (
<div>
<span ref={this.props.ref2}>forwardRef(类式组件)</span>
</div>
)
}
};
export const Sunan4 = forwardRef((props, ref) => <Sunan4_ ref2={ref} {...props} />);
结束语
更多react
经验文章,请点击苏南大叔的博客文章:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。