常德网站开发网站运营,北京小程序 app开发公司,炫酷的网站开发,网站备案怎么换ref 概述
拿一个场景举例开始 比如#xff0c;在react当中写一个组件#xff0c;类型是 class Component在 render function 里面渲染一系列的子组件或者 dom节点会有这样的需求#xff0c;就是获取某个dom节点或某个子组件的实例来做一些手动的操作不局限于 props 更新这种…ref 概述
拿一个场景举例开始 比如在react当中写一个组件类型是 class Component在 render function 里面渲染一系列的子组件或者 dom节点会有这样的需求就是获取某个dom节点或某个子组件的实例来做一些手动的操作不局限于 props 更新这种方式来更新这个节点获取节点后可以绑定某些事件做一些操作如果没有其他好方法可能要自己去写 document.querySelector之类的选择器来获取这个 dom 节点 在 react 当中就为我们提供了这种方式 通过 ref 这个特殊的 attribute 可以非常方便的在一个组件内部获取到它的一个子节点的实例对象, 这就是 ref 的一个核心功能 在 react 当中有三种使用ref的方式 string reffunctioncreateRef
ref 应用示例
1 ) 演示代码
import React from reactexport default class RefDemo extends React.Component {constructor() {super()this.objRef React.createRef()}componentDidMount() {setTimeout(() {this.refs.stringRef.textContent string refthis.methodRef.textContent method refthis.objRef.current.textContent obj ref}, 1000)}render() {return (p refstringRefxxxx/pp ref{ele (this.methodRef ele)}yyyy/pp ref{this.objRef}zzzz/p/)}
}2 代码说明
第一种使用了 stringRef 也就是我们在想要获取的那个节点的 props上面使用了一个 ref 属性, 然后传入一个字符串, react在完成这一个节点的渲染之后它会在 this.refs 这个对象上面挂载这个string对应的一个key这个key所指向的就是我们这个节点实例的对象如果是dom节点, 它就会对应dom的实例如果是子组件就是子组件的实例即class Component如果是一个 function Component正常来讲它是会失败的拿到的会是一个 undefined因为 function Component 没有实例如果不让其出错需要使用 forwardRef后续来说 * 此种方式不被推荐 第二种使用了 function 在 ref 属性上传入一个 method 方法它接受一个参数这个参数就是一个节点对应的实例 如果是dom节点对应的是dom实例如果是组件对应的是组件的实例 然后在this上面去挂载某一个属性比如上面的 methodRef, 基于其 textContent 属性来重新赋值 第三种是通过 React.createRef 这个API 在 class Component 内部我们使用 this.objRef React.createRef() 去创建了一个对象 这个对象相当于 { current: null} , 默认值是 null 把这个对象传给某一个节点的 ref 属性上在组件渲染完成之后会把这个节点对应的实例挂载到这个对象的 current 这个属性上面调用它就是通过 this.objRef.current 操作它就可以了 这个 demo 效果是不用跑也知道是怎样的一个变化以上三种情况的前两种没有在源码中有过多的体现我们看下第三种情况 createRef
3 源码探究 从入口文件 React.js 中可见 import {createRef} from ./ReactCreateRef;定位到 ReactCreateRef.js 中 /*** Copyright (c) Facebook, Inc. and its affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.* flow*/import type {RefObject} from shared/ReactTypes;// an immutable object with a single mutable value
export function createRef(): RefObject {const refObject {current: null,};if (__DEV__) {Object.seal(refObject);}return refObject;
}这个代码看上去非常简单它返回的就是一个简单的对象 DEV 相关的判断不是核心忽略即可 它里面的属性 current, 默认值是 null
forward ref
1 示例演示 关于 forward ref, 先看一个示例 import React from reactconst TargetComponent React.forwardRef((props, ref) (input typetext ref{ref} /
))export default class Comp extends React.Component {constructor() {super()this.ref React.createRef()}componentDidMount() {this.ref.current.value ref input}render() {return TargetComponent ref{this.ref} /}
}在这个文件下定义了两个组件 Comp, TargetComponentComp 组件 里面创建了一个ref, 并传递给 TargetComponent 组件ref 是为了去获取某一个节点的实例的通常会获取dom节点的实例有时也会获取一下class Component 的实例如果组件是一个 纯的 function Component, 没有实例则会出错 如果我是一个组件的提供者就是开源了一些组件用户(开发者) 一开始并不知道是否是有实例的组件还有就是比如一些包, 如 Redux提供的 connect 方法里的 HOC (高阶组件)作为用户想要获取自己写的组件的实例的时候传入ref获得的是被包装过的组件 解决之道是外面套上 React.forwardRef如上代码所示 forwardRef 可以帮助我们实现一个 ref 的传递 TargetComponent 组件 就是我们基于 React.forwardRef 实现的 function Component如果要把它改造成HOC, 也可以基于此函数的回调将 ref 进行传递挂载这样就不会违背开发者的意图
2 源码分析
定位到 forwardRef.js 中
/*** Copyright (c) Facebook, Inc. and its affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.*/import {REACT_FORWARD_REF_TYPE} from shared/ReactSymbols;import warningWithoutStack from shared/warningWithoutStack;export default function forwardRefProps, ElementType: React$ElementType(render: (props: Props, ref: React$RefElementType) React$Node,
) {if (__DEV__) {if (typeof render ! function) {warningWithoutStack(false,forwardRef requires a render function but was given %s.,render null ? null : typeof render,);} else {warningWithoutStack(// Do not warn for 0 arguments because it could be due to usage of the arguments objectrender.length 0 || render.length 2,forwardRef render functions accept exactly two parameters: props and ref. %s,render.length 1? Did you forget to use the ref parameter?: Any additional parameter will be undefined.,);}if (render ! null) {warningWithoutStack(render.defaultProps null render.propTypes null,forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?,);}}return {$$typeof: REACT_FORWARD_REF_TYPE,render,};
}忽略 DEV 判断代码其本质上就只有一个 return 对象 return {$$typeof: REACT_FORWARD_REF_TYPE,render,
};$$typeoff是 REACT_FORWARD_REF_TYPE 注意 使用 forwordRef 时, 用这个API最终创建出来的 ReactElement比如上面示例的 TargetComponent组件最终会被创建成一个 ReactElement比如别名叫 ATargetComponent组件 就是 forwordRef 返回的对象这个不难理解因为代码如此TargetComponent 对应的 组件A 的 type 是 TargetComponentTargetComponent 对应的 组件A的 $$typeof 是 REACT_ELEMENT_TYPE A的 $$typeof 不是 REACT_FORWARD_REF_TYPE因为所有通过React.createElement创建的节点的 $$typeof 都是 REACT_ELEMENT_TYPETargetComponent组件最终会被React.createElement创建出来这一点不要搞混了 render 就是我们传进来的 function component