美文网首页
React新特性走一波

React新特性走一波

作者: 雪燃归来 | 来源:发表于2020-06-22 10:04 被阅读0次

一、Context与ContextType

       Context与ContextType用于解决编程开发效率问题。

(一)、Context

1、定义

       Context提供一种方式,能够让数据在组件树中传递而不必一级一级手动传递。

Context
       Context由生产者Provider和消费者Consumer组成。有点类似于我们定义的全局变量。
Context结构
1、基本使用
import React, { Component,createContext } from 'react';

+const BatteryContext = createContext()

class Leaf extends Component{
  render(){
    return (
+      <BatteryContext.Consumer>
+        {
+          battery => <h1>Battery: {battery}</h1>
+        }
+      </BatteryContext.Consumer>
    )
  }
}

class Middle extends Component{
  render(){
    return <Leaf/>
  }
}

class App extends Component{
  render(){
    return (
      <div>
+        <BatteryContext.Provider value={60}>
+          <Middle/>
+        </BatteryContext.Provider>
      </div>
      )
  }
}
export default App;
运行结果
2、动态改变值
class App extends Component{
  state = {
    battery: 60
  }
  render(){
    const { battery } = this.state
    return (
      <div>
+        <BatteryContext.Provider value={battery}>
+          <button 
+          onClick={() => this.setState({battery: battery -1})}>Press</button>
+          <Middle/>
+        </BatteryContext.Provider>
      </div>
      )
  }
}
动态改变值
3、多生产者多消费者的情况
import React, { Component,createContext } from 'react';

+const BatteryContext = createContext()
+const OnlineContext = createContext()

class Leaf extends Component{
  render(){
    return (
+      <BatteryContext.Consumer>
+        {
+         battery => (
+            <OnlineContext.Consumer>
+              {
+               online => <h1>Battery: {battery}, Online: {String(online)}</h1>
+              }
+            </OnlineContext.Consumer>
+          )
+        }
+      </BatteryContext.Consumer>
    )
  }
}

class Middle extends Component{
  render(){
    return <Leaf/>
  }
}

class App extends Component{
  state = {
    battery: 60,
    online: false
  }
  render(){
    const { battery, online } = this.state
    return (
      <div>
+        <BatteryContext.Provider value={battery}>
+          <OnlineContext.Provider value={online}>
+            <button 
+            onClick={() => this.setState({battery: battery -1})}>Press</button>
+            <button
+              onClick={() => this.setState({online: !online})}
+              >Switch</button>
+            <Middle/>
+          </OnlineContext.Provider>
+        </BatteryContext.Provider>
      </div>
      )
  }
}
export default App;

       如果组件找不到对应的值,并不会报错,而会启用Provider的默认值。

import React, { Component,createContext } from 'react';

const BatteryContext = createContext(90)

class Leaf extends Component{
  render(){
    return (
      <BatteryContext.Consumer>
        {
          battery => (
            <h1>Battery: {battery}</h1> 
          )
        }
      </BatteryContext.Consumer>
    )
  }
}

class Middle extends Component{
  render(){
    return <Leaf/>
  }
}

class App extends Component{
  state = { }
  render(){
    const { battery } = this.state
    return (
      <div>
        {/* <BatteryContext.Provider value={battery}> */}
            <button 
            onClick={() => this.setState({battery: battery -1})}>Press</button>
            <Middle/>
        {/* </BatteryContext.Provider> */}
      </div>
    )
  }
}
export default App;

(二)、ContextType

       Context会让组件变得不纯粹,因为依赖了全局变量。所以这决定了Context一般不会大规模的使用。所以一般在一个组件中使用一个Context就好。

       由于Consumer的特性,里面的代码必须是这个函数的返回值。这样就显得复杂与不优雅了。那该怎么解决呢?这样contextType就派上用场了。

首先我们用static来声明contextType:

static contextType = BatteryContext;

这样在运行时就可以获取到一个新的属性。我们来接收他。

const battery = this.context;

这样Consumer就可以完全不再使用了。

 return<h1>Battery : {battery}</h1>

完整代码

import React, { Component,createContext } from 'react';

+const BatteryContext = createContext(90)

class Leaf extends Component{
+ static contextType = BatteryContext;

+ render(){
+    const battery = this.context
+    return (
+    <h1>Battery: {battery}</h1>
+    )
  }
}

class Middle extends Component{
  render(){
    return <Leaf/>
  }
}

class App extends Component{
  state = { }
  render(){
    const { battery } = this.state
    return (
      <div>
        {/* <BatteryContext.Provider value={battery}> */}
            <button 
            onClick={() => this.setState({battery: battery -1})}>Press</button>
            <Middle/>
        {/* </BatteryContext.Provider> */}
      </div>
    )
  }
}
export default App;
效果图

       效果和使用Consumer没有什么区别。可见只有一个Context的时候,使用contextType要比使用Consumer简单的多。

二、Lazy、Suspen和ErrorBoundary

       Lazy、Suspen用于解决运行时性能问题。
       在平常的开发中,我们需要用到延迟加载的技术,比如懒加载,这样可以提升我们的性能。React项目中,我们可以通过下面两个步骤实现按需加载

1、通过webpack的代码分割(Code Spliting )
2、import 异步导入组件

具体流程如下:
1、通过lazy异步导入组件

const About = lazy(() => import('./About.jsx'))

2、通过Suspense添加加载中状态,消除空档期错误

render(){
    return (
      <div>
        <Suspense fallback={<div>loading</div>}>
          <About></About>
        </Suspense>
      </div>
    )
  }

3、修改webpack异步导入组件数据,修改打包后文件名称

const About = lazy(() => import(/*webpackChunkName:"about"*/'./About.jsx'))
修改前
修改后
2、通过ErrorBoundary 捕获加载中错误
       要捕获加载中错误,我们可以在生命周期钩子函数componentDidCatch进行错误处理
class App extends Component{
  state = {
    hasError : false
  }

  componentDidCatch(){
    this.setState({
      hasError: true
    })
  }

  render(){
    if(this.state.hasError){
      return <div>Error</div>
    }
    return (
      <div>
        <Suspense fallback={<div>loading</div>}>
          <About></About>
        </Suspense>
      </div>
    )
  }
}

也可以通过getDerivedStateFromError静态方法实现错误处理

static getDerivedStateFromError(){
    return {
      hasError: true
    }
}

完整代码如下:
App.jsx

import React, { Component, lazy, Suspense } from 'react'

+const About = lazy(() => import(/*webpackChunkName:"about"*/'./About.jsx'))

class App extends Component{
+  state = {
+    hasError : false
+  }
+  static getDerivedStateFromError(){
+    return {
+      hasError: true
+    }
+  }
  // componentDidCatch(){
  //   this.setState({
  //     hasError: true
  //   })
  // }

  render(){
+    if(this.state.hasError){
+      return <div>Error</div>
+    }
    return (
      <div>
        <Suspense fallback={<div>loading</div>}>
          <About></About>
        </Suspense>
      </div>
    )
  }
}

export default App

About.jsx

import React, { Component } from 'react'

class About extends Component{
    render(){
        return (<div>About</div>)
    }
}

export default About

三、PureConponent、Memo实现指定组件进行渲染

       当一个组件中包含子组件的时候,父组件数据的变化会导致子组件render函数的执行,如果父组件变化的数据子组件根本没有用到,那么子组件这样的更新显然是不合理的,怎么来解决这个问题了。

1、子组件使用shouldComponentUpdate钩子函数实现

shouldComponentUpdate(nextProps, nextState){
    if(nextProps.name === this.props.name){
      return false
    } 
    return true
  }

完成代码

import React, { Component } from 'react'

class Foo extends Component{
+  shouldComponentUpdate(nextProps, nextState){
+    if(nextProps.name === this.props.name){
+      return false
+    } 
+    return true
+  }

  render(){
    console.log('Foo header')
    return (
    <div>{this.props.name}-{this.props.count}</div>
    )
  }
}

class App extends Component{
  state = {
    count: 0,
    name: 'antiai'
  }
  render(){
    const { count, name } = this.state
    return (
      <div>
        <button onClick={() => this.setState({ count : count + 1})}>add</button>
        <Foo count={count} name={name}/>
      </div>
    )
  }
}
export default App

       不管怎么点击button,Foo组件中render函数就在最开始执行了一次,以后不再执行,因为button点击事件并没有修改掉App组件的name属性。

2、子组件使用PureComponent组件实现

       1、PureComponent对于简单的数据类型或者对象类型第一层的改变,执行结果跟shouldComponentUpdate相同

import React, { Component, PureComponent } from 'react'

class Foo extends PureComponent{
  
  render(){
    console.log('Foo header')
    return (
    <div>{this.props.name}</div>
    )
  }
}

class App extends Component{
  state = {
    count: 0,
  }
  render(){
    const {count} = this.state
    return (
      <div>
        <button onClick={() => this.setState({ count : count + 1})}>add</button>
         <Foo name='Mike' count={count}/>
      </div>
    )
  }
}
export default App

       此时要注意,子组件中的render函数的执行是根据父组件传入的值是否发生改变而执行的,pureComponent中,我们并不能像shouldComponentUpdate中一样针对特定属性的更改来确定组件是否重新渲染。
       2、PureComponent对于多层数据结构的改变(对象)只会监测到第一层,也就是说第二层的改变不会影响到一层的改变。此处person中的age发生了改变,但是子组件接受的是person,所以没有监测到数据的变化。

import React, { Component, PureComponent } from 'react'

class Foo extends PureComponent{
  
  render(){
    console.log('Foo header')
    return (
      <div>{this.props.person.age}</div>
    )
  }
}

class App extends Component{
  state = {
    count: 0,
    person: {
      age: 1
    }
  }
  render(){
    const {count, person} = this.state
    return (
      <div>
        <button onClick={() => {
          person.age ++
          this.setState({
            count: count + 1
          })
        }}>
          Add</button>
        <Foo person={person}/>
      </div>
    )
  }
}
export default App

       要解决上面的问题有下面两种用法
方法一:给Foo组件传递回调函数cb

import React, { Component, PureComponent } from 'react'

class Foo extends PureComponent{
  
  render(){
    console.log('Foo header')
    return (
      <div>{this.props.person.age}</div>
    )
  }
}

class App extends Component{
  state = {
    count: 0,
    person: {
      age: 1
    }
  }

  render(){
    const { person} = this.state
    return (
      <div>
        <button onClick={() => {
          person.age ++
          this.setState({
            person
          })
        }}>
          Add</button>
        <Foo person={person} cb={() => {}}/>
      </div>
    )
  }
}

export default App

       在上面,我们给Foo组件额外传递了一个cb函数,以为每次函数都是新的,所以,每次都会触发子组件更新。
方法二:使用memo组件

import React, { Component, memo } from 'react'

const Foo = memo(function Foo(props){
  console.log('Foo render')
  return <div>{props.person.age}</div>
})

class App extends Component{
  state = {
    count: 0,
    person: {
      age: 1
    }
  }

  callback = () => { }

  render(){
    const { person} = this.state
    return (
      <div>
        <button onClick={() => {
          person.age ++
          this.setState({
            person
          })
        }}>
          Add</button>
        <Foo person={person} cb={() => {}}/>
      </div>
    )
  }
}
export default App

       React.memo()是一个高阶函数,它与 React.PureComponent类似,但是一个函数组件而非一个类。

总结:

1、pureComponent 提供简单的对比算法,避免组件重新渲染,减少性能开销
2、无状态组件是函数式的,不能继承PureComponent,可以使用memo达到相同的效果

推论
       拆分越细的组件,传入属性越简单,使用PureComponentmemo的机会越多。

相关文章

  • React新特性走一波

    一、Context与ContextType Context与ContextType用于解决编程开发效率问题。 (一...

  • React Native面向未来架构

    既然面向未来,那么项目的架构设计采用React/React native的新特性,并且为即将来到的新特性预留位置;...

  • React Hooks 精讲

    这篇文章主要介绍 React 新特性 Hooks 以及应用场景。 React Hooks 是 React 的一个新...

  • React 新特性

    React 中一个常见模式是为一个组件返回多个元素。Fragments 可以让你聚合一个子元素列表,并且不在DOM...

  • React新特性

    1、React新特性介绍 React v16.0 render 支持返回数组和字符串、Error Boundari...

  • 状态逻辑复用:React Hooks 与 Vue Functio

    React Hooks 是 React16.8 引入的新特性,支持在类组件之外使用 state、生命周期等特性。 ...

  • React 16.6.X版本的更新功能

    React发布了16.6版本,在此版本中带来了一些非常有用的新特性。主要的新特性包括: React.lazy() ...

  • React Hooks的入门简介

    什么是React Hooks? 首先React Hooks是React生态圈里的新特性,它改变了传统react的开...

  • react hook介绍

    react hook是什么 react hook是react中引入新特性,它可以让react函数组件也拥有状态;通...

  • 一文归纳 React Hooks 常用场景

    前言 React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看来,使用 React Ho...

网友评论

      本文标题:React新特性走一波

      本文链接:https://www.haomeiwen.com/subject/yqbtfktx.html