# React中refs的理解 `Refs`提供了一种方式,允许我们访问`DOM`节点或在`render`方法中创建的`React`元素。 ## 概述 在典型的`React`数据流中,`props`是父组件与子组件交互的唯一方式,要修改一个子组件,你需要使用新的`props`来重新渲染它,但是在某些情况下,你需要在典型数据流之外强制修改子组件,被修改的子组件可能是一个`React`组件的实例,也可能是一个`DOM`元素,对于这两种情况`React`都提供了解决办法。 避免使用`refs`来做任何可以通过声明式实现来完成的事情,通常在可以使用`props`与`state`的情况下勿依赖`refs`,下面是几个适合使用`refs`的情况: * 管理焦点、文本选择或媒体播放。 * 触发强制动画。 * 集成第三方`DOM`库。 ## 使用 `React`提供的这个`ref`属性,表示为对组件真正实例的引用,其实就是`ReactDOM.render()`返回的组件实例,需要区分一下渲染组件与渲染原生`DOM`元素,渲染组件时返回的是组件实例,而渲染`DOM`元素时,返回是具体的`DOM`节点,`React`的`ref`有`3`种用法。 ### 字符串 `ref`可以直接设置为字符串值,这种方式基本不推荐使用,或者在未来的`React`版本中不会再支持该方式。这主要是因为使用字符串导致的一些问题,例如当`ref`定义为`string`时,需要`React`追踪当前正在渲染的组件,在`reconciliation`阶段,`React Element`创建和更新的过程中,`ref`会被封装为一个闭包函数,等待`commit`阶段被执行,这会对`React`的性能产生一些影响等。 ``` class InputOne extends React.Component { componentDidMount() { this.refs.inputRef.value = 1; } render() { return ; } } ``` ### 回调 `React`支持给任意组件添加特殊属性,`ref`属性接受一个回调函数,其在组件被加载或卸载时会立即执行。 * 当给`HTML`元素添加`ref`属性时,`ref`回调接收了底层的`DOM`元素作为参数。 * 当给组件添加`ref`属性时,`ref`回调接收当前组件实例作为参数。 * 当组件卸载的时候,会传入`null`。 * `ref`回调会在`componentDidMount`或`componentDidUpdate`等生命周期回调之前执行。 `Callback Ref`我们通常会使用内联函数的形式,那么每次渲染都会重新创建,由于`React`会清理旧的`ref`然后设置新的,因此更新期间会调用两次,第一次为`null`,如果在`Callback`中带有业务逻辑的话,可能会出错,可以通过将`Callback`定义成类成员函数并进行绑定的方式避免。 ``` class InputTwo extends React.Component { componentDidMount() { this.inputRef.value = 2; } render() { return this.inputRef = element} />; } } ``` ### API创建 在`React v16.3`中经`0017-new-create-ref`提案引入了新的`React.createRef`的`API`,当`ref`被传递给`render`中的元素时,对该节点的引用可以在`ref`的`current`属性中被访问,`ref`的值根据节点的类型而有所不同: * 当`ref`属性用于`HTML`元素时,构造函数中使用`React.createRef()`创建的`ref`接收底层`DOM`元素作为其`current`属性。 * 当`ref`属性用于自定义`class`组件时,`ref`对象接收组件的挂载实例作为其`current`属性。 * 不能在函数组件上使用`ref`属性,因为他们没有实例。 对比新的`CreateRef`与`Callback Ref`,并没有压倒性的优势,只是希望成为一个便捷的特性,在性能上会会有微小的优势,`Callback Ref`采用了组件`Render`过程中在闭包函数中分配`ref`的模式,而`CreateRef`则采用了`Object Ref`。 ``` class InputThree extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } componentDidMount() { this.inputRef.current.value = 3; } render() { return ; } } ``` ## 示例 ```