简要介绍:React16.3中修改了Refs的使用方法,并且提供了Forwarding Refs用于在父组件中操作子组件中的dom节点,本文介绍一下新的Refs表示方法以及Forwarding Refs。
Ref的功能就不具体说了,就是在父组件中直接操作子组件的方法,Ref的方式,使其脱离了props传值,然后更新子组件的标准方法。
1 . React16.3中的Refs
(1)创建
16.3版本中Refs通过React.createRef()的方法创建,通过ref属性关联,比如我们创建一个Child子组件,并在该组件中使用Ref:
class Child extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
//React16.3中创建Ref的方法
}
render(){
return <input ref={this.myRef}/>
}
}
上述是创建Ref指向的方法,在Ref所指向的组件,在render后就可以调用,React16.3中提供了current属性,用于引用render后的节点:
componentDidMount(){
console.log(this.myRef.current);
//render之后就可以输出该ref指向的那个节点
}
此外,同样的Ref所指向的节点可以是dom节点,也可以是类组件。
(2)Ref属性指向的节点不能是函数组件
因为我们通过ref获得的组件,包含了声明周期和state,因此ref所指向的组件不可以是函数组件。
function MyFunctionComponent(){
return <div>1</div>
}
class Child extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
render(){
return <MyFunctionComponent ref={this.myRef}/>
//报错Warning: Stateless function components
//cannot be given refs. Attempts to access this
//ref will fail.
}
}
但是在函数组件中,是可以通过ref的方式指向一个类组件或者dom节点的。
2 .暴露子组件中的dom节点给父组件
(1)使用场景
在某些场景中, 我们可能需要在一个父组件中操作自组件中的dom节点,结构层级为:
父组件——>子组件——>子组件中的DOM节点
我们想在父组件中,直接操作子组件中的dom节点,这种操作不是很推荐,因为可能破坏了组件的封装性,标准的父子组件通信一般是通过Props传值的形式,但是有些时候有这种直接操作dom节点的需求,比如需要使dom节点聚焦,在父组件中需要知道子组件中某个dom节点的位置等等情况。
此时,如果我们仅仅在父组件中,在子组件的引用上,增加属性ref,那么这个ref仅仅指向的是子组件,而不能知道子组件中的dom节点,举例来说:
子组件为:
class Child extends React.Component{
constructor(props){
super(props);
}
render(){
return <input />
}
}
父组件中,ref:
class Father extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <Child ref={this.myRef}/>
}
}
输出的myRef为:
Child {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}
也就是说指向的是子组件,而不能指向子组件中的dom节点。
(2)React16.3中的Forwarding refs
通过React16.3中的Forwarding refs可以使得在父组件中可以得到子组件中的dom节点。
Forwarding refs中提供了一个React.forwardRef来创建组件,在React.forwardRef的方法中传递了参数ref,通过这个ref可以指向具体的某一个dom节点。具体的指向流程为:
父组件myRef——>React.forwardRef中的实参——>通过forwardRef方法创建的子组件中的ref——>指向子组件中的某一个dom节点
举例来说:
子组件(通过forwardRef方法创建)
const Child=React.forwardRef((props,ref)=>(
<input ref={ref} />
));
父组件:
class Father extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <Child ref={this.myRef}/>
}
}
此时的myRef输出为:
input
我们一步步来看上述的过程是如何发生的。
- 首先在Father组件中通过React createRef的方式创建一个myRef
- 将myRef在子组件Child中通过制定ref={myRef}的形式传递下去
- 在子组件的构造方法中,会接受传递过来的ref作为实参,然后这个实参的ref可以复制给子组件中的dom节点等。
- 当父组件的myRef关联相应的dom节点之后,就可以通过current属性在父组件中得到子组件的dom节点。
(3)在高阶组件HOC中的Forwarding Refs
我们来创建一个简单的HOC:
function logProps(WrappedComponent) {
class logProps extends React.Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
return logProps;
}
这个HOC,用于构建一个新组件,在新组建中可以输出传入组件的所有属性信息。
我们令Child组件为:
class Child extends React.Component{
constructor(props){
super(props);
}
render(){
return <input />
}
}
我们在Child组件的基础上,通过logProps生成一个高阶组件:
const logProps=logProps(Child);
在父组件中通过ref获取子组件:
class Father extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <LogProps ref={this.myRef}/>
}
}
此时输出的是LogProps而不是Child,父组件的myRef指向的是HOC生成的组件。
LogProps {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}
同样的,如果我们想要父组件中的myRef指向HOC的原始组件,那么需要改写HOC的构造函数:
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;
// Assign the custom prop "forwardedRef" as a ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// Note the second param "ref" provided by React.forwardRef.
// We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
// And it can then be attached to the Component.
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
此时,输出的myRef为:
Child
此外,虽然React中通过React.createRef()的方式来创建ref,但是并没有废除回调的方式来创建ref的方法。