React ref的使用示例

写了一段时间的 react,99%都在写 state、prop、useState、useEffect,对 ref 特别不熟悉,前几天做一个需求,想用 ref 实现父组件捞子组件的某个状态值,结果失败了,特此整理一下 ref 相关内容。

什么是 ref

官网介绍:

在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。被修改的子组件可能是一个 React 组件的实例,也可能是一个 DOM 元素。对于这两种情况,React 都提供了解决办法,即使用 ref 来获取 dom 或组件实例。

如何使用 ref

放在 dom 元素上

这是 ref 最直接的用法

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = createRef()
  }
  componentDidMount() {
    console.log(this.myRef)
  }
  render() {
    return <div ref={this.myRef}>测试</div>
  }
}

打印看一下 ref 是啥

可以看出,ref.current 拿到了 dom 元素,所以我们可以实现 dom 元素本身的一些功能,如 input 的聚焦:

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = createRef()
  }

  onClick = () => {
    this.myRef.current.focus()
  }

  render() {
    return (
      <div>
        <button onClick={this.onClick}>聚焦</button>
        <input ref={this.myRef} />
      </div>
    )
  }
}

官网还提供了一种 ref 回调的形式:

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = null
  }

  onClick = () => {
    this.myRef.focus()
  }

  render() {
    return (
      <div>
        <button onClick={this.onClick}>聚焦</button>
        <input ref={ele => this.myRef = ele} /> // 这里的 ele 就是该 dom 元素
      </div>
    )
  }
}

放在类组件上

其实组件跟原生 dom 差不多,也是拥有自己的 ui、一些功能的某种元素,所以将 ref 放在组件上,也可以获取到该组件的示例。

// 子组件
class Child extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: 'xx'
    }
  }
  render() {
    return <div>子元素{this.state.name}</div>
  }
}

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = createRef()
  }

  componentDidMount() {
    console.log(this.myRef)
  }

  render() {
    return (
     <Child ref={this.myRef} />
    )
  }
}

那既然可以获取到子组件的实例,我们就可以操作子组件了,比如文章最开始说,我想在父组件里去捞子组件的某些状态值。

class Child extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }

  onClick = () => {
    this.setState({count: this.state.count+1})
  }

  render() {
    return <button onClick={this.onClick}>点击+1:{this.state.count}</button>
  }
}

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = createRef()
  }

  onClick = () => {
    console.log(this.myRef.current.state.count) // 拿到子组件的状态值
  }

  render() {
    return (
      <div>
        <button onClick={this.onClick}>获取子组件的点击次数</button>
        <Child ref={this.myRef} /> // ref 获取到子组件实例
      </div>
    )
  }
}

既然能拿值,我也能拿函数去修改子组件

class Child extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: 'xx'
    }
  }
  changeName = () => {
    this.setState({name: 'ww'})
  }
  render() {
    return <div>子元素{this.state.name}</div>
  }
}

export class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = createRef()
  }

  onClick = () => {
    this.myRef.current.changeName() // 父组件的手伸到子组件里去啦
  }

  render() {
    return (
      <div>
        <button onClick={this.onClick}>改变子组件的状态</button>
        <Child ref={this.myRef} />
      </div>
    )
  }
}

当然这个例子并不恰当,父组件想更改子组件的状态的话,应该把状态提升到父组件中,然后作为子组件的props传递进去。
主要是 ref 提供一种方式去绕过 props 来实现父子组件通信。

放在函数组件上

这是我文章开头写需求时犯的错,ref 不能放在函数组件上,因为函数组件没有实例。

const Child = () => {
  return <div>子组件</div>
}

export const Demo = () => {
  const myRef = useRef() // 可以在函数组件内创建 ref

  useEffect(() => {
    console.log(myRef)
  }, [])

  return <Child ref={myRef} /> // 但是放在函数组件上无效
}

那函数组件就不能使用 ref 了吗,那肯定不是哈哈。我们可以使用 forwardRef 包装函数组件。

const Child = (props, ref) => { // 包装后,除了原有的 props 外, ref 也被传了进来
  return <div ref={ref}>子组件</div> // 还是得挂载到 dom 上
}

const ProChild = React.forwardRef(Child) // 重点在这里

export const Demo = () => {
  const myRef = useRef()

  useEffect(() => {
    console.log(myRef)
  }, [])

  return <ProChild ref={myRef} />
}

这里贴一下官网的 tip:

那既然函数组件也可以使用 ref 的话,我们用函数组件实现一下父组件捞子组件的数据,不过可以看出,使用 forwardRef 包裹后,ref 还是得挂载到 dom 或者类组件上,如果我只想挂载数据还需要搭配 useImperativeHandle。

const Child = (props, ref) => {
  const [count, setCount] = useState(0)

  useImperativeHandle(
    ref,
    () => ({   // 这里就是暴露给外部 ref 的数据
      getVal: ()=> count
    }),
    [count],
  )

  const onClick = () => {
    setCount(pre => pre+1)
  }
  return <button onClick={onClick}>点击+1:{count}</button>
}

const ProChild = React.forwardRef(Child)

export const Demo = () => {
  const myRef = useRef()

  const onClick = () => {
    console.log(myRef.current.getVal()) // 拿到子组件的值
  }

  return <><button onClick={onClick}>获取子组件的点击次数</button><ProChild ref={myRef} /></>
}

至此完成了做需求时留下的问题 ✅

总结

最后还是需要强调一下,父组件获取子组件状态的场景,一般还是状态提升 + 回调来通信,需求最终也是使用这种方式来实现的,最开始之所以想用 ref,是觉得状态提升后,子组件变化了会引起父组件的重新渲染,但是我只想拿数据而不引起渲染。
跟师傅说了一下我写需求时的想法,师傅见解如下:

  • 优先考虑状态提升
  • 有性能问题的话,考虑状态提升 + memo
  • 不想给多个组件加 memo 的话,就要考虑引入 redux/mobx 了
  • 如果引入 redux/mobx 是一种成本的话,那 ref 也不是不可以哈哈哈

以上就是React ref的使用详解的详细内容,更多关于React ref的使用的资料请关注我们其它相关文章!

(0)

相关推荐

  • React Native中的RefreshContorl下拉刷新使用

    我们知道App中都有下拉加载,在React Native中也有类似的控件 一.属性方法 (1) onRefresh function 在视图开始刷新的时候调用 (2) refreshing bool 视图是否在刷新时显示指示器,也表明当前是否在刷新中 (3) colors [ColorPropType] android平台适用 进行设置加载进去指示器的颜色,至少设置一种,最多可以设置4种 (4) enabled bool android平台适用 用来设置下拉刷新功能是否可用 (5) progre

  • 学习React中ref的两个demo示例

    为了摆脱繁琐的Dom操作, React提倡组件化, 组件内部用数据来驱动视图的方式,来实现各种复杂的业务逻辑 ,然而,当我们为原始Dom绑定事件的时候, 还需要通过组件获取原始的Dom, 而React也提供了ref为我们解决这个问题. 为什么不能从组件直接获取Dom? 组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM).只有当它插入文档以后,才会变成真实的 DOM 如果需要从组件获取真实 DOM 的节点,就要用到官方提供的ref属性 使

  • 详解react-refetch的使用小例子

    使用react-refetch来简化api获取数据的代码 const List = ({data: gists}) => { return ( <ul> {gists.map(gist => ( <li key={gist.id}>{gist.description}</li> ))} </ul> ) } const withData = url => Part => { return class extends Component

  • React组件refs的使用详解

    ref顾名思义我们知道,其实它就可以被看座是一个组件的参考,也可以说是一个标识.作为组件的属性,其属性值可以是一个字符串也可以是一个函数. 其实,ref的使用不是必须的.即使是在其适用的场景中也不是非用不可的,因为使用ref实现的功能同样可以转化成其他的方法来实现.但是,既然ref有其适用的场景,那也就是说ref自有其优势.关于这一点和ref的适用场景,官方文档中是这样说的: 在从 render 方法中返回 UI 结构之后,你可能想冲出 React 虚拟 DOM 的限制,在 render 返回的

  • React中Ref 的使用方法详解

    本文实例讲述了React中Ref 的使用方法.分享给大家供大家参考,具体如下: React中Ref 的使用 React v16.6.3 在典型的React数据流中,props是父组件与其子组件交互的唯一方式.要修改子项,请使用new props 重新呈现它.但是,在某些情况下,需要在典型数据流之外强制修改子项.要修改的子项可以是React组件的实例,也可以是DOM元素.对于这两种情况,React都提供了api. 何时使用refs refs有一些很好的用例: 1.文本选择或媒体播放. 2.触发势在

  • React中的refs的使用教程

    ref是React中的一种属性,当render函数返回某个组件的实例时,可以给render中的某个虚拟DOM节点添加一个ref属性,如下面的代码所示: <body> <script type="text/jsx"> var App = React.createClass({ render: function() { return ( <div> <input type="text" placeholder="inp

  • React ref的使用示例

    写了一段时间的 react,99%都在写 state.prop.useState.useEffect,对 ref 特别不熟悉,前几天做一个需求,想用 ref 实现父组件捞子组件的某个状态值,结果失败了,特此整理一下 ref 相关内容. 什么是 ref 官网介绍: 在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式.要修改一个子组件,你需要使用新的 props 来重新渲染它.但是,在某些情况下,你需要在典型数据流之外强制修改子组件.被修改的子组件可能是一个 React 组件

  • React Native Popup实现示例

    目录 具体实现 使用方法 React Native 官方提供了 Modal 组件,但 Modal 是属于全屏的弹出层,当 Modal 显示时,操作区域只有 Modal 里的元素,而且焦点会被 Modal 劫持.虽然移动端不常见,但有些场景还是希望可以用轻量级一点的 Popup. 在 React Native 里,元素的层级是不可以被穿透的,子元素无论如何都不能遮挡父元素.所以选择了在顶层添加 Popup,设置绝对定位,显示时根据指定元素来动态调整 Popup 的位置的方案. 具体实现 Popup

  • ahooks解决React闭包问题方法示例

    引言 本文是深入浅出 ahooks 源码系列文章的第三篇,这个系列的目标主要有以下几点: 加深对 React hooks 的理解. 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库. 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择. 注:本系列对 ahooks 的源码解析是基于 v3.3.13.自己 folk 了一份源码,主要是对源码做了一些解读,可见 详情. 系列文章: 大家都能看得懂的源码 ahooks 整体架构篇 如何使用插件化机制优雅的封装你的请求

  • react fiber执行原理示例解析

    目录 为什么要使用fiber,要解决什么问题? fiber是什么? 数据结构 执行单元 浏览器工作: Fiber执行原理 workInProgress tree: currentFiber tree: Effects list: render阶段: 遍历节点过程: 收集effect list: commit阶段: 为什么commit必须是同步的操作的? 为什么要使用fiber,要解决什么问题? 在 react16 引入 Fiber 架构之前,react 会采用递归方法对比两颗虚拟DOM树,找出需

  • ES6下React组件的写法示例代码

    本文主要跟大家分享了ES6下React组件的写法示例,下面来一起看看详细的介绍: 一:定义React组件 class Hello extends React.Component { render() { return <h1>Hello, {this.props.value}</h1>; } } 二:声明prop类型与默认prop class Hello extends React.Component { // ... } Hello.propTypes = { value: Re

  • React Hook的使用示例

    这篇文章分享两个使用React Hook以及函数式组件开发的简单示例. 一个简单的组件案例 Button组件应该算是最简单的常用基础组件了吧.我们开发组件的时候期望它的基础样式能有一定程度的变化,这样就可以适用于不同场景了.第二点是我在之前做项目的时候写一个函数组件,但这个函数组件会写的很死板,也就是上面没有办法再绑定基本方法.即我只能写入我已有的方法,或者特性.希望编写Button组件,即使没有写onClick方法,我也希望能够使用那些自带的默认基本方法. 对于第一点,我们针对不同的class

  • GraphQL在react中的应用示例详解

    目录 什么是 GraphQL GraphQL出现的意义 传统API存在的主要问题: GraphQL 如何解决问题 GraphQL基本语法 标量类型 对象类型 枚举类型 GraphQL 内置指令 什么是 Apollo apollo-server 处理流程 1.解析阶段 2.校验阶段 3.执行阶段 Schema 给server端带来的便利性 创建client 将client注入到react 数据请求 数据缓存 apollo-client 总结 GraphQL 的优缺点 优点 缺点 什么是 Graph

  • React Streaming SSR原理示例深入解析

    目录 功能简介 基本原理 使用示例 Streaming HTML Selective Hydration 降级逻辑 JS 和 CSS 设置 源码解析 数据结构 Segment Boundary Task Request 主要流程 功能简介 React 18 提供了一种新的 SSR 渲染模式: Streaming SSR.通过 Streaming SSR,我们可以实现以下两个功能: Streaming HTML:服务端可以分段传输 HTML 到浏览器,而不是像 React 18 以前一样,需要等待

  • c++中ref的作用示例解析

    目录 正文 示例1: 输出: 输出: 总结 正文 C++11 中引入 std::ref 用于取某个变量的引用,这个引入是为了解决一些传参问题. 我们知道 C++ 中本来就有引用的存在,为何 C++11 中还要引入一个 std::ref 了?主要是考虑函数式编程(如 std::bind)在使用时,是对参数直接拷贝,而不是引用.下面通过例子说明 示例1: #include <functional> #include <iostream> void f(int& n1, int&

  • react组件基本用法示例小结

    本文实例讲述了react组件基本用法.分享给大家供大家参考,具体如下: 组件间传值: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <div id="app"> </div> &l

随机推荐