【查漏补缺系列-React篇】Ref、useContext、HOC

前言: 本类文章初衷只用作记录个人的学习博客,哪里有漏补哪里,不做任何其他商业用途。欢迎讨论,不喜勿喷。后面要是有遗漏的相关知识点,也会相应的补上。如果本篇文章能帮到你,我也会很愉快,共勉😁

目录

  1. Ref
  2. useContext
  3. HOC

Ref

ref的特点:

  • 更改ref的值时不会触发重新渲染
  • 每次页面、组件重新渲染时ref的值是不会改变的
  • 可以在除渲染过程以外的场景下修改和更新ref的值
  • 在渲染期间(未初始化完成)无法访问、修改ref的值

class与hooks的不同写法

class写法

js 复制代码
import React, { Component, createRef } from 'react'

export default class ClassBasic extends Component {
  constructor(props) {
    super(props);
    this.inpRef = createRef();
  }

  handleClick = () => {
    // 点击按钮 文本框获取焦点
    this.inpRef.current.focus();
  }

  render() {
    return (
      <div>
        <input ref={this.inpRef} />
        <button onClick={this.handleClick}>Click me</button>
      </div>
    )
  }
}

hook写法

js 复制代码
import React, { useRef } from 'react'

export default function HooksBasic() {
  const inpRef = useRef();

  const handleClick = () => {
    // 点击按钮 文本框获取焦点
    inpRef.current.focus();
  }
  return(
    <div>
        <input ref={inpRef} />
        <button onClick={handleClick}>Click me</button>
    </div>
  )
}

forwardRef

ref 本身是不能跨层级捕获和传递的,forwardRef 可以接收父元素的ref信息,转发下去,在父组件中即可直接获取子组件的元素。如下,在父组件点击button后子组件的input获取焦点:

js 复制代码
import React, { useRef, forwardRef } from 'react'

const Child = forwardRef((props, ref) => {
  return (<div>
    <input ref={ref} />
  </div>)
})

export default function Main() {
  const childRef = useRef();

  const handleClick = () => {
    childRef.current.focus();
  }
  return(
    <div>
      <Child ref={childRef} />
      <button onClick={handleClick}>focus</button>
    </div>
  )
}

useImperativeHandle

在父组件中,直接调用子组件的方法,该hook接收三个参数:

  • ref: ref 接收forwardRef 传递进来的ref
  • createHandle: 返回暴露给父组件的 ref 对象
  • deps: 更新ref对象的依赖
jsx 复制代码
import React, { useRef, forwardRef, useImperativeHandle } from 'react'

const Child = forwardRef((props, ref) => {
  const inpRef = useRef();

  const focus = () => {
    inpRef.current.focus();
  }

  const changeValue = (value) => {
    inpRef.current.value = value;
  }

  useImperativeHandle(ref, () => ({ focus, changeValue  }))

  return (<div>
    <input ref={inpRef} />
  </div>)
})

export default function Main() {
  const exposeRef = useRef(null);

  const handleFocus = () => {
    exposeRef.current.focus();
  }

  const handleChangeValue = () => {
    exposeRef.current.changeValue('Hello World')
  }

  return(
    <div>
      <Child ref={exposeRef} />
      <button onClick={handleFocus}>InputFocus</button>
      <button onClick={handleChangeValue}>ChangeValue</button>
    </div>
  )
}

useContext

作用: 可以读取和订阅组件中的context

useContext(SomeContext)

参数:使用createContext方法创建的context,context 本身不包含信息,它只代表你可以提供或从组件中读取的信息类型。

返回值:useContext为调用组件返回context的值。它被确定为传递给树中调用组件上方最近的SomeContext.Providervalue。如果没有这样的provider,那么返回值将会是创建context时赋予的默认值。返回的值始终是最新的。如果context发生变化,React会自动重新渲染读取context组件。

js 复制代码
import React, { useState, useContext, createContext } from 'react'

const TargetContext = createContext(null)

const ChildA = () => {
  const num = useContext(TargetContext)
  return (<div>
    <p>ChildA-{ num }</p>
  </div>)
}

const ChildB = () => {
  const num = useContext(TargetContext)
  return (<div>
    <ChildA />
    <p>ChildB-{ num }</p>
  </div>)
}

export default function Main() {
  const [num, setNum] = useState(10)
  return(
    <TargetContext.Provider value={num}>
      <ChildB />
      {/* 当num发生变化时,会重新渲染包裹在Context中的所有组件并赋予更新后的值 */}
      <button onClick={() => setNum(num * 2)}>update</button>
    </TargetContext.Provider>
  )
}

HOC

HOC的优点

  • 逻辑、组件复用,减少过多的重复代码,提升可读性
  • 控制组件的渲染逻辑
  • 可代理使用HOC组件的生命周期

属性继承

对被处理组件的props进行代理劫持

js 复制代码
import React from 'react'

const withCard = (color) => (Component) => {
  const PrivateComponent = (props) => {
    const style = {
      fontSize: '20px',
      color
    }

    return <div style={style}>
      {/* 被处理组件的props一定会有一个type属性 */}
      <Component {...props} type={ 'hoc' } />
    </div>
  }
  return PrivateComponent;
}

const ChildA = withCard('blue')((props) => {
  return (<p>childA--{props.type}</p>)
})

const ChildB = withCard('yellow')((props) => {
  return (<p>childB--{props.type}</p>)
})

export default function App() {
  return(
    <section>
      <ChildA />
      <ChildB />
    </section>
  )
}

反向继承

反向继承会返回一个继承了传入组件的类组件,并在render函数中调用super.render()。这样做的好处是可以通过高阶组件之间访问到传入组件的this,操作组件中的state和生命周期钩子等等。

js 复制代码
import React from 'react'

const logProps = (logMap) => {
  return (WrappedComponent) => {
    const didMount = WrappedComponent.prototype.componentDidMount;
    return class PrivateComponent extends WrappedComponent {
      componentDidMount() {
        if (didMount) {
          didMount.apply(this)
        }

        Object.entries(logMap).forEach(([k, v]) => {
          if (document.getElementById(k)) {
            console.log(v, this.uname) // Text Message  Dane
          }
        })
      }

      render() {
        return super.render();
      }
    }
  }
}

class Index extends React.Component {
  constructor() {
    super();
    this.uname = 'Dane'
  }
  render() {
    return (
      <div>
        <p id="text">Hello World</p>
      </div>
    )
  }
}

const LogIndex = logProps({'text': 'Text Message'})(Index)

export default function App() {
  return(
    <section>
      <LogIndex />
    </section>
  )
}
相关推荐
如若12315 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~1 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语1 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport1 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg1 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww1 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254881 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234522 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js
Web阿成2 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript