【查漏补缺系列-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>
  )
}
相关推荐
拖孩21 分钟前
【Nova UI】十五、打造组件库之滚动条组件(上):滚动条组件的起步与进阶
前端·javascript·css·vue.js·ui组件库
Hejjon34 分钟前
Vue2 elementUI 二次封装命令式表单弹框组件
前端·vue.js
小堃学编程1 小时前
前端学习(3)—— CSS实现热搜榜
前端·学习
Wannaer2 小时前
从 Vue3 回望 Vue2:响应式的内核革命
前端·javascript·vue.js
不灭锦鲤2 小时前
xss-labs靶场基础8-10关(记录学习)
前端·学习·xss
Bl_a_ck2 小时前
--openssl-legacy-provider is not allowed in NODE_OPTIONS 报错的处理方式
开发语言·前端·web安全·网络安全·前端框架·ssl
懒羊羊我小弟2 小时前
手写符合Promise/A+规范的Promise类
前端·javascript
互联网搬砖老肖2 小时前
Web 架构之负载均衡会话保持
前端·架构·负载均衡
赵大仁2 小时前
React vs Vue:点击外部事件处理的对比与实现
javascript·vue.js·react.js
肥肥呀呀呀3 小时前
在Flutter上如何实现按钮的拖拽效果
前端·javascript·flutter