React高级指引

南山隐士 2022年06月25日 30 0

React.lazy

当我们应用比较庞大的时候, 又或者说当你当前组件的文件比较大,我们可以将组件进行赖加载, 在某些情况下, 它才会被加载使用, 这个时候 我们可以使用 React.lazy 配合 Suspense 使用

import React, { Suspense } from 'react';

const LazyPage = React.lazy(() => import('../lazy/Lazy'));


function MyLazyPage() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyPage />
      </Suspense>
    </div>
  );
}

export default MyLazyPage

以上代码, lazyPage组件 将会 懒加载, 在加载过程中, 将会显示 Suspense 定义的fallback 组件

配合Route使用

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

React.lazy 目前只支持==默认导出==(default exports), 如果你想通过==命名导出的方式==

可以参考官方这个例子
image.png

默认导出, 就是你导出的时候, 导出的数据你可以自己想怎么命名都可以, 但是命名导出, 你导出的名称必须一致

Context

当你某个属性会在多个组件中使用到的时候, 我们可以考虑使用Context, 它可以避免你一层层地往下传递值, 它有点类似于Vue当中 Provider和 inject

要想实现这个功能,我们首先得创建一个context对象
你可以通过 React.createContext(初始的值) 来 创建一个 context 对象, 它还会返回 ProviderConsumer 对象, 它们都是组件, 你可以使用它,

const nameContext = React.createContext('evanyou') // 创建一个context, 默认值为evanyou

Provider

Provider 接受一个value 值, value 值可以更新初始值的, 当value 值发生变化时, 它底下的所有组件都会重新渲染, Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数

<nameContext.Provider value="yaojin">
   <Second />
   <OtherPage />
 </nameContext.Provider>

将默认的evanyou 值更新成yaojin, 需要注意的是, 尽量不要将值写在value中, 如果value 是一个对象 那么都会不断返回一个新的对象, 其底下的组件都会重新渲染, 此时可以考虑将该值放到父组件的state 中

如何读取呢 ?
通过 contextType 属性 可以获取到离最近的那个匹配的 Provider 中读取context的值 Provider 重新渲染,

class Second extends React.Component {
  static contextType = nameContext

  render() {
    return <div>
      {this.context}
      <Transmit value={this.context}/>
    </div>
  }
}

==contextType 属性== 仅用在 class 组件中

Consumer

那在函数组件应该如何读取我需要的值呢 ? 我们还可以使用 Consumer, 在其内部, 你需要使用一个函数来接受context的值, 这个函数应该做为子元素

function OtherPage() {
  return <nameContext.Consumer>
    {value => <div
    >{value}
    </div>
    }
  </nameContext.Consumer>
}

Ref

有时候,我们想操作真实的DOM元素,例如获得某个input的焦点, 或者说或许某个组件的实例, ref 可以帮我们实现这一点

通过 React.createRef() 创建一个ref

在元素或者组件中通过 ref属性 来绑定对应的ref

==ref 只能用于 在 class 组件中使用, 不能再函数组件中使用==

class GetDom extends React.Component {
  constructor(props) {
    super(props)
    // 创建一个ref
    this.inputRef = React.createRef()
    this.focusTextInput = this.foucusInput.bind(this)
  }

  foucusInput() {
    console.log('父组件通过ref拿到该组件实例, 直接调用这个方法了')
    // 通过current可以访问到ref绑定的dom元素或者class组件
    this.inputRef.current.focus()
  }

  render() {
    return (
      <div>
        {/* ref获取inputDom元素 */}
        <input ref={this.inputRef} />
      </div>
    )
  }
}


export default class GetClass extends React.Component {
  constructor (props) {
    super(props)
    this.classRef = React.createRef()
  }

  componentDidMount() {
    this.classRef.current.foucusInput()
  }

  render () {
    return (
      <div> 
        {/* ref获取GetDom组件实例 */}
        <GetDom ref={this.classRef} />
      </div>
    )
  }
}

不同于通过React.createRef()你还可以通过 ==回调 Refs== 来存储 ref, 在需要使用的时候, 调用它

export default class GetClass extends React.Component {
  constructor (props) {
    super(props)
    this.refInfo = null
    this.cacheRef = refData => {
      this.refInfo = refData;
    }
    this.triggerChildrenFun = () => {
      if (this.cacheRef) {
        this.refInfo.foucusInput ()
      }
    }
  }

  componentDidMount() {
    this.triggerChildrenFun()
  }

  render () {
    return (
      <div> 
        {/* ref获取GetDom组件实例 */}
        <GetDom ref={this.cacheRef} />
      </div>
    )
  }
}

Fragments

render 需要 创建多个元素的时候,都需要使用 div 来包裹, 如果不想使用这个div来包裹元素, 可以使用 Fragments, 它类似于一个<> 的标签包裹你的元素, 它不支持key 或者 传递属性

如果你想使用key 属性 , 你可以显示得使用 <React.Fragment> 包裹元素

Hoc

Hoc在React中称为高阶组件, 高阶组件的作用在于一些公用逻辑放到高阶组件中, 它本质上其实就是一个函数组件,但是它接受另外的组件作为参数, 并返回该组件

通过高阶组件, 我们可以把公用的方法以及state都提取都高阶组件中, 在通过props的方式传递到对应的组件中,

function HocWrap(title) {
  return WrapComponent => {
    return class extends React.Component {
      // 修改在 React-devtool 中高阶组件名称
      static displayName = `HocWrap(${WrapComponent})`

      constructor(prosp) {
        super(prosp)
        this.state = {
          username: '',
          password: '',
          rePassword: '',
        }
        this.onChange = this.onChange.bind(this)
        this.composeChange = this.composeChange.bind(this)
      }

      onChange(stateName, stateValue) {
        this.setState({
          [stateName]: stateValue,
        })
      }

      handleSubmit = e => {
        e.preventDefault()
        const { username, password, rePassword } = this.state
        if (rePassword) {
          alert(
            `用户名: ${username}, 密码: ${password}, 确认密码: ${rePassword}`,
          )
        } else {
          alert(`用户名: ${username}, 密码: ${password}`)
        }
      }

      composeChange(name) {
        return e => this.onChange(name, e.target.value)
      }

      render() {
        // 抽离出公用的方法
        const mapFunToProps = {
          composeChange: this.composeChange,
          handleSubmit: this.handleSubmit,
        }

        return (
          <div>
            <h1>{title}</h1>
            <WrapComponent {...this.state} {...mapFunToProps} />
          </div>
        )
      }
    }
  }
}

在高阶组件中, 如果你想拿到 被包装 组件的实例, 你可以使用ref转发 来实现, React 提供了一个 React.forwardRef 方法来创建一个组件, 这个方法, 不但可以接受props, 还可以接受ref作为参数, 然后我们就可以 通过 React.createRef 来创建 ref , 将这个ref 传递到 React.forwardRef 方法中

// Hoc组件内部是一个函数组件 最终返回一个React.forwardRef创建的组件, 它里面返回的是高阶组件中被包裹的组件, 并将ref作为props进行传递
return React.forwardRef((props, ref) => {
      return <HocRef forwardedRef={ref} />
    })

因为高阶组件是函数组件, 我们需要通过一个层class 包裹, 因为ref 只有 class 组件可以使用, 只有它可以创建一个ref, 创建完之后我们才可以通过 React.forwardRef 让函数组件接受到这个ref, 有了这个ref ,我们就可以通过props的形式传递到内部的包裹组件了, 可以看上面的代码, 我们将ref, 通过props 传递给包裹组件了, 那么内部包裹组件就可以在通过props读取到ref, 通过ref属性 进行绑定关联了

==只需要明确 ref 只有在class组件中创建并且使用, ref不能像props一样传递给子组件, 所以当遇到函数组件的时候, 我们想拿到这个ref,就可以通过React.forwardRef来接收ref, 又或者说你想将这个ref往下传递==

// 向外暴露的是高阶组件的返回值~包装了 Register 组件返回了一个新组件
const Hoc = HocWrap('注册')(Register)

export default class Wrap extends React.Component {
  render() {
    const ref = React.createRef()
    return (
      <div>
        <Hoc ref={ref}></Hoc>
      </div>
    )
  }
}

Portals

ReactDOM.createPortal 可以让元素插入到别的dom节点上, 不是单单地插入到父节点上

ReactDOM.createPortal(child, container)

第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素

Last Updated: 2022/06/25 19:16:45
React基础速览 dva速览