React 进阶(二)ref 和高阶组件

ref

ref 用于直接操作 DOM,有三种方式:

  • ref 字符串
  • ref 对象(推荐,但是不能使用在函数式组件上)
  • ref 回调函数
import { PureComponent, createRef } from 'react'

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.titleRef = createRef()
    this.titleEl = ''
  }

  changeText1() {
    this.refs.titleRef.innerHTML = 'Hello React'
  }
  changeText2() {
    this.titleRef.current.innerHTML = 'Hello React'
  }
  changeText3() {
    this.titleEl.innerHTML = 'Hello React'
  }

  render() {
    return (
      <div>
        {/* 字符串 */}
        <h2 ref="titleRef">Hello World</h2>
        <button onClick={() => this.changeText1()}>改变文本1</button>
        {/* 对象,推荐方式 */}
        <h2 ref={this.titleRef}>Hello World</h2>
        <button onClick={() => this.changeText2()}>改变文本2</button>
        {/* 回调函数 */}
        <h2 ref={(arg) => (this.titleEl = arg)}>Hello World</h2>
        <button onClick={() => this.changeText3()}>改变文本3</button>
      </div>
    )
  }
}

export default App

forwardRef

函数组件没有实例,不能使用 ref,需要使用 forwardRef:

import { PureComponent, createRef, forwardRef } from 'react'

const About = forwardRef(function (props, ref) {
  return <h2 ref={ref}>About</h2>
})

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.aboutRef = createRef()
  }

  printRef() {
    console.log(this.aboutRef.current)
  }

  render() {
    return (
      <div>
        <About ref={this.aboutRef} />
        <button onClick={() => this.printRef()}>打印ref</button>
      </div>
    )
  }
}

export default App

受控组件

表单数据由 React 来管理的组件:

import { PureComponent } from 'react'

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.state = { username: '' }
  }

  handleSubmit(e) {
    e.preventDefault()
  }
  handleChange(e) {
    this.setState({ username: e.target.value })
  }

  render() {
    return (
      <div>
        <form onSubmit={(e) => this.handleSubmit(e)}>
          <label htmlFor="username">
            用户:
            <input
              type="text"
              id="username"
              value={this.state.username}
              onChange={(e) => this.handleChange(e)}
            />
          </label>
          <input type="submit" value="提交" />
        </form>
      </div>
    )
  }
}

export default App

高阶组件(HOC)

高阶组件是参数为组件,返回值为新组件的函数。

应用场景:

  • props 增强
  • 渲染判断鉴权
  • 生命周期劫持
import { PureComponent } from 'react'

function enhanceComponent(WrappedComponent) {
  class NewComponent extends PureComponent {
    render() {
      return <WrappedComponent />
    }
  }
  NewComponent.displayName = 'Kobe'
  return NewComponent
}

class App extends PureComponent {
  render() {
    return <div>App</div>
  }
}

const EnhanceComponent = enhanceComponent(App)
export default EnhanceComponent

createPortal

将子节点渲染到 root 节点以外:

import { PureComponent } from 'react'
import { createPortal } from 'react-dom'

class Modal extends PureComponent {
  render() {
    return createPortal(this.props.children, document.getElementById('modal'))
  }
}

class App extends PureComponent {
  render() {
    return (
      <div>
        <h2>Home</h2>
        <Modal>
          <h2>This is Modal</h2>
        </Modal>
      </div>
    )
  }
}

export default App

Fragment

类似 Vue 的 template,避免多余的 DOM 包裹:

import { PureComponent, Fragment } from 'react'

class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <h2>Hello</h2>
        <h2>React</h2>
      </Fragment>
      // 短语法:<>...</>
    )
  }
}

export default App

StrictMode

用来突出显示应用程序中潜在问题的工具,不会渲染任何可见的 UI(仅在开发模式下运行):

  • 识别不安全的生命周期
  • 关于使用过时字符串 ref API 的警告
  • 关于使用废弃的 findDOMNode 方法的警告
  • 检测意外的副作用(constructor 会调用两次)
  • 检测过时的 context API
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
)