React高级指引

React.lazy

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

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使用react-router

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), 若是你想经过==命名导出的方式==dom

能够参考官方这个例子 ide

image.png

默认导出, 就是你导出的时候, 导出的数据你能够本身想怎么命名均可以, 可是命名导出, 你导出的名称必须一致函数

Context

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

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

const nameContext = React.createContext('evanyou') // 建立一个context, 默认值为evanyou
复制代码

Provider

Provider 接受一个value 值, value 值能够更新初始值的, 当value 值发生变化时, 它底下的全部组件都会从新渲染, Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数spa

<nameContext.Provider value="yaojin">
   <Second /> <OtherPage /> </nameContext.Provider> 复制代码

将默认的evanyou 值更新成yaojin, 须要注意的是, 尽可能不要将值写在value中, 若是value 是一个对象 那么都会不断返回一个新的对象, 其底下的组件都会从新渲染, 此时能够考虑将该值放到父组件的state 中3d

如何读取呢 ? 经过 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 元素

相关文章
相关标签/搜索