react16的新特性(一)

主要包括以下特性的增加:错误边界、render方法新增返回类型、Fragment、createContext、createRef、forwardRef、生命周期函数的更新、lazy、suspense、Portals、memo、Strict Mode

错误边界 Error Boundary

错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。

  • 如果一个 class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界组件。
//最佳实践:将ErrorBoundary抽象为一个公用的组件类
import React ,{Component} from 'react';
export default ErrorBoundary extends Component{
  constructor(props){
    super(props);
    this.state={hasError:false};
  }
  componentDidCatch(err,info){
    //如果调用了这个函数就说明子组件抛出错误了,改变状态,渲染备用的组件
    this.setState({hasError:true});//修改状态,通知出错了
    console.log(err,info);//打印错误信息
  }
  render(){
    if(this.state.hasError){
      return 
throw error
} return this.props.children } }

使用错误边界的组件

import React ,{Component} from 'react';
export default class App extends Component{
  constructor(){
    this.state={user:'aa'}
  }
  changeState=()=>{
    this.setState({
      user:null
    })
  }
  render(){
    return 
} }
//可能出错的子组件
export default class CouldThrowErrComponent extends Component{
  constructor(props){
    super(props);
  }
  componentDidUpdate(){
    return throw new Error('抛错')
  }
  render(){
    let {user} =this.props;
    return 
{user}
} }
  • 错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
  • 错误边界仅可以捕获其子组件的错误
  • 无法捕捉 1. 异步的抛错(事件,计时器等)、2。服务端渲染、3.自身抛错
  • 一般使用static getDerivedStateFromError() 来渲染一个提示错误的UI,使用componentDidCatch() 来记录一些error的详细信息,错误调用栈等等

render方法新增返回类型

render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的DOM层级

//string
render(){
    return 'hello,world'
}

//number
render(){
    return 12345
}

//boolean
render(){
    return isTrue?true:false
}

//null
render(){
    return null
}

//fragments,未加key标识符,控制台会出现warning
render(){
    return [
        
hello
, world,

oh

] }

Fragment

Fragment 组件其作用是可以将一些子元素添加到 DOM tree 上且不需要为这些元素提供额外的父节点,相当于 render 返回数组元素。

render() {
  return (
    
      Some text.
      

A heading

More text.

Another heading

Even more text.
); }

如果使用数组需要加key,而且更麻烦

render() {
  return (
      [
      "Some text.",
      

A heading

, "More text.",

Another heading

, "Even more text." ] ); }

createContext / Provider / contextType / Consumer

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。顶层的Provider中传入value,在子孙级的Consumer中获取该值,并且能够传递函数,用来修改context

  • React.createContext 是一个函数,它接收初始值并返回带有 Provider 和 Consumer 组件的对象;
  • Provider 组件是数据的发布方,一般在组件树的上层并接收一个数据的初始值;
  • Consumer 组件是数据的订阅方,它的 props.children 是一个函数,接收被发布的数据,并且返回 React Element;
    以前跨组件传递数据都是通过逐层传递
class App extends React.Component {
  render() {
    return ;
  }
}

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    
); } class ThemedButton extends React.Component { render() { return

使用context,避免中间元素传递props

 const ThemeContext = React.createContext(
  "dark" // 默认值
);
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      
        
      
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
  return (
    
); } // 类组件一般使用添加属性的方式接受数据 class ThemedButton extends React.Component { // 指定 contextType 读取当前的 theme context。 // React 会往上找到最近的 theme Provider,然后使用它的值。 // 在这个例子中,当前的 theme 值为 “dark”。 static contextType = ThemeContext; render() { return

函数式组件一般使用Consumer接收数据

//创建Context组件
const ThemeContext = React.createContext({
  theme: 'dark',
  toggle: () => {}, //向上下文设定一个回调方法
});

//运行APP
class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggle = () => { //设定toggle方法,会作为context参数传递
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };

    this.state = {
      theme: themes.light,
      toggle: this.toggle,
    };
  }

  render() {
    return (
       //state包含了toggle方法
        
      
    );
  }
}

//中间组件
function Content() {
  return (
    
); } //接收组件 function Button() { return ( {({theme, toggle}) => ( )} ); }

createRef / forwardRef

通过ref只能拿到标签元素或者通过class创建的react元素,不能拿到通过函数创建的react元素
适用于表单获取、元素动画等场景

class Child extends React.Component{
  constructor(props){
    super(props);
    this.myRef=React.createRef();//创建ref对象
  }
  componentDidMount(){
    console.log(this.myRef.current);//通过current拿到ref元素input标签
  }
  render(){
    return //挂载ref绑定的元素
  }
}

React.forwardRef 是 Ref 的转发, 它能够让父组件访问到子组件的 Ref,从而操作子组件的 DOM。

//把FancyButton上的ref转发到button上
const FancyButton = React.forwardRef((props, ref) => (
  
));
class App extends from Component{
  constructor(props){
    super(props);
   // 你可以直接获取 DOM button 的 ref:
    this.myRef = React.createRef();
  }
   componentDidMount(){
    console.log(this.myRef.current)//拿到button 
   }
  render(){
    return 
Click me!
} }

回调 Refs

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    this.setTextInputRef = element => {
      this.textInput = element;
    };
    this.focusTextInput = () => {
      // 使用原生 DOM API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }
  componentDidMount() {
    // 组件挂载后,让文本框自动获得焦点
    this.focusTextInput();
  }
  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
      
); } }

生命周期函数的更新

  • React 引入了 getDerivedStateFromProps 、 getSnapshotBeforeUpdate 及 componentDidCatch 等三个全新的生命周期函数。
  • 将 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 标记为不安全的方法。
getDerivedStateFromProps(nextProps, prevState) 
//不仅在 props 更新时会被调用,setState 时也会被触发

getSnapshotBeforeUpdate(prevProps, prevState)
//会在组件更新之前获取一个快照,
//并可以将计算得的值或从 DOM 得到的信息传递到 componentDidUpdate(prevProps, prevState, snapshot) 函数的第三个参数
//常常用于 scroll 位置定位等场景。

componentDidCatch() 
//函数让开发者可以自主处理错误信息,诸如错误展示,上报错误等

lazy/suspense

React.lazy() 提供了动态 import 组件的能力,实现代码分割。
Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。

import React, { Component, lazy, Suspense } from 'react';
const LazyTest1 = lazy(() => import('./components/LazyTest.1'));
const LazyTest2 = lazy(() => import('./components/LazyTest.2'));

class App extends Component {
  fallback = () =>{
    return (
      
Loading...
); } render() { return (

懒加载组件

); } }

Suspense 可以放在懒加载的组件外层的任意位置,fallback 是懒加载组件载入过程中的一个过渡,可以放一些过渡效果或方法。

Portals

使用createPortal将组件渲染到当前组件树之外,我们可以将组件渲染到我们想要的任意DOM节点中,但该组件依然处在React的父组件之内。使用于弹窗、浮层类的组件

//实现一个简易蒙层效果,抽象出一个通用的Overlay组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default class Overlay extends Component {
    constructor(props) {
        super(props);
        this.container = document.createElement('div');
        document.body.appendChild(this.container);
    }
    componentWillUnmount() {
        document.body.removeChild(this.container);
    }
    render() {
        return ReactDOM.createPortal(//将组件放到了创建的container上
            
× {this.props.children}
, this.container ) } } //该组件对应的样式如下 .overlay{ box-sizing:border-box; position: fixed; top:50%; left:50%; width:260px; height:200px; margin-left:-130px; margin-top:-100px; padding:10px; background-color: #fff; outline: rgba(0,0,0,.5) solid 9999px; } .overlay-close{ position: absolute; top:10px; right:10px; color:red; cursor: pointer; }

使用方式

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      overlayActive: false
    }
    this.closeOverlay = this.closeOverlay.bind(this);
    this.showOverlay = this.showOverlay.bind(this);
  }
  closeOverlay() {
    this.setState({ overlayActive: false })
  }
  showOverlay() {
    this.setState({ overlayActive: true })
  }
  render() {
    return (
      
hello world!
{this.state.overlayActive && overlay content}
); } }

memo / PureComponent(类似vue的keepAlive)

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

情景:

import React  from 'react';

export default class extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            date : new Date()
        }
    }

    componentDidMount(){
        setInterval(()=>{
            this.setState({
                date:new Date()
            })
        },1000)
    }
    render(){
        return (
            
{this.state.date.toString()}
) } }

现在有一个显示时间的组件,每一秒都会重新渲染一次,对于Child组件我们肯定不希望也跟着渲染,所有需要用到PureComponent

class Child extends React.PureComponent {
    render(){
        console.log('I am rendering');
        return (
            
I am update every {this.props.seconds} seconds
) } }

现在新出了一个React.memo()可以满足创建纯函数而不是一个类的需求

function Child({seconds}){
    console.log('I am rendering');
    return (
        
I am update every {seconds} seconds
) }; export default React.memo(Child)

React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()功能类似。
此方法仅作为性能优化的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 bug。

import React from "react";

function Child({seconds}){
    console.log('I am rendering');
    return (
        
I am update every {seconds} seconds
) }; function areEqual(prevProps, nextProps) { if(prevProps.seconds===nextProps.seconds){ return true//返回true就不刷新 }else { return false//返回false就刷新 } } export default React.memo(Child,areEqual)

Strict Mode

启用严格模式下:

  • 识别被标志位不安全的生命周期函数
  • 对弃用的 API 进行警告
  • 探测某些产生副作用的方法
  • 检测是否使用 findDOMNode
  • 检测是否采用了老的 Context API
class App extends React.Component {
  render() {
    return (
      
) } }

你可能感兴趣的:(react16的新特性(一))