React HOC(高阶组件 && dva 构建 react 项目&& umi && React的生命周期@stage3---week5--day1

HOC(高阶组件)

dva 构建 react 项目 DvaJS

umi 构建项目

React 的生命周期



HOC(高阶组件)

功能:

​ 1. 进行某些方法或是属性的复用

​ 2. 让外层的组件替我们完成任务,那么里层组件直接使用就可以了

const NewComponent = higherOrderComponent(YourComponent);

比如,我们想要我们的组件通过自动注入一个版权信息。

案例 1

// withCopyright.js 定义一个高阶组件
import React, { Component, Fragment } from "react";

const withCopyright = WrappedComponent => {
  return class NewComponent extends Component {
    render() {
      return (
        <Fragment>
          <WrappedComponent />
          <div>&copy;版权所有 千锋教育 2019 </div>
        </Fragment>
      );
    }
  };
};
export default withCopyright;
// 使用方式
import withCopyright from "./withCopyright";

class App extends Component {
  render() {
    return (
      <div>
        <h1>Awesome React</h1>
        <p>React.js是一个构建用户界面的库</p>
      </div>
    );
  }
}
const CopyrightApp = withCopyright(App);

案例 2 有参数传递

import React, { Component } from "react";
import { CSSTransition } from "react-transition-group";
import "animate.css";

export const withAnimateHoc = Comp => {
  // * 定义的参数只有组件参数
  return class _ extends Component {
    render() {
      const { flag, timeout, classNames } = this.props; //* 通过结构父组件传进来的参数,来获取所需要的变量
      return (
        <CSSTransition
          in={flag} // ! in取值 true/false ,这个值必须是变化的
          timeout={timeout} // ! 延迟时间
          classNames={classNames} // ! 这里是我们具体动画的属性
          unmountOnExit // ! 当exit退出时,组件卸载掉
        >
          <Comp />
        </CSSTransition>
      );
    }
  };
};
  • 使用
import React, { Component } from "react";
import { AnimateHoc } from "../hoc/index";
import Comp from "./Comp";
import "animate.css";
import { CSSTransition } from "react-transition-group";

const Hoc = AnimateHoc(Comp); //* 高阶组件实例化,并赋值给Hoc

export default class AnimateComp extends Component {
  constructor(props) {
    super(props);

    this.state = {
      flag: true,
      timeout: 200,
      classNames: {
        enter: "animated",
        enterActive: "slideInLeft",
        exit: "animated",
        exitActive: "slideOutLeft"
      }
    };
  }

  changeFlag = () => {
    // console.log("张浩雨: AnimateHoc", AnimateHoc)
    const { flag } = this.state;
    console.log("flag 已经改变");
    this.setState({
      flag: !flag
    });
  };

  render() {
    const { flag } = this.state;

    return (
      <div>
        <button onClick={this.changeFlag}>
          {" "}
          changeFlag changeFlag changeFlag{" "}
        </button>
        <Hoc {...this.state}></Hoc> //组件使用,并已属性的形式进行传餐
        <CSSTransition
          in={flag}
          timeout={2000}
          classNames={{
            enter: "animated",
            enterActive: "rollIn",
            exit: "animated",
            exitActive: "rollOut"
          }}
        >
          <p> 动画元素 </p>
        </CSSTransition>
      </div>
    );
  }
}

dva 构建 react 项目 DvaJS

  1. 安装 dva-cli
$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.1
  1. 创建新应用
$ dva new dva-quickstart
  1. 这会创建 dva-quickstart 目录,包含项目初始化目录和文件,并提供开发服务器、构建脚本、数据 mock 服务、代理服务器等功能。

    然后我们 cd 进入 dva-quickstart 目录,并启动开发服务器:

$ cd dva-quickstart
$ npm start

umi 构建项目

https://pro.ant.design/docs/getting-started

  1. $ yarn create umi
  2. yarn 会先安装最新版的 create-umi,然后提供交互式的提示来创建应用。
  3. $ yarn //然后安装依赖,
  4. $ yarn start //启动项目

React 的生命周期

React 中组件有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化、运行中、销毁、错误处理(16.3 之后)
注意: 生命周期钩子函数一定不要写成箭头函数

React 生命周期

  • React 15.x 10 个钩子函数
    • 初始化
    • 运行中
    • 销毁
  • React 16.x 10 个钩子函数
    • 初始化
    • 运行中
    • 销毁
    • 错误处理
  • React 17.x 还没有发布 【 未来版本 】 9 个钩子函数
    • 初始化
    • 运行中
    • 销毁
    • 错误处理

一、 初始化阶段
import React, { Component } from "react";

export default class LifeCompInitial extends Component {
  //?  React 16.x 生命周期初始化阶段
  constructor(props) {
    // todo 1. 继承父组件属性,然后将props赋值给this.props  2. 定义状态
    console.log("1-constructor");
    super(props);

    this.state = {
      info: "千锋教育"
    };
  }
  componentWillMount() {
    // todo 组件即将挂载 - 为组件的事件和生命周期钩子做准备
    // * 类似 vue 中 beforeCrete
    // * 这里我们在项目中不使用
    console.log("2-componentWillMount");
  }

  render() {
    // todo  render函数作用
    //  * 1. 利用React.createElement() api将jsx解析为虚拟DOM对象模型
    //  * 2. 计算 this.state 和  this.props Í
    console.log("3-render");
    const { info } = this.state;
    const { name } = this.props;
    return (
      <div>
        {info}
        <p> App组件传递给我一个属性: {name} </p>
      </div>
    );
  }

  componentDidMount() {
    //? 组件挂在结束
    //todo  componentDidMount作用:
    //   * 1. 数据请求,数据修改
    //   * 2. 操作DOM
    console.log("4-componentDidMount");
    fetch("/mock/index.json")
      .then(data => {
        // console.log( "张浩雨: LifeCompInitial -> componentDidMount -> data", data )
        return data.json();
      })
      .then(res => {
        console.log("张浩雨: LifeCompInitial -> componentDidMount -> res", res);
        /* 修改数据 */
        this.setState({
          info: res.name
        });
      })
      .catch(err => console.log(err));
    // todo DOM操作
    document.querySelector("p").style.background = "#ccc";
  }
}
二、 运行阶段
import React, { Component } from "react";

export default class LifeCompRunning extends Component {
  constructor(props) {
    super(props);

    this.state = {
      info: "你们是最帅最美的"
    };
  }

  changeInfo = () => {
    this.setState({
      info: "你们是最苦最累"
    });
  };
  // ! 组件的运行阶段  1. props改变触发【 外部传入 】  2. state改变触发

  componentWillReceiveProps(nextProps) {
    //* 即将接收新的属性   nextProps就是新的属性
    //todo 作用: 新属性的接收
    // ! 路由的监听, 比如当路由改变时,app项目中的头部或是底部的显示
    console.log("1 - componentWillReceiveProps", nextProps);
  }

  shouldComponentUpdate() {
    console.log("shouldComponentUpdate");
    //todo 组件是否要更新,它决定了组件是否要渲染,是react性能优化的关键
    //* 一定要有返回值,返回值为true,那么更新,false,则不更新
    //* 默认不写的话,就是return true
    return true;
  }
  componentWillUpdate() {
    console.log("componentWillUpdate");
    // * 组件即将更新
    //* 这个狗子函数做的就是更新前的准备工作
    // * 这个钩子中不能使用 this.setState(), 否则会栈溢出
  }

  render() {
    console.log("render ");
    /* render 1. jsx -> VDOM对象模型   2. 计算 this.props   this.state  */
    /* 这个钩子函数中不能使用 this.setState(),否则会造成栈溢出 */
    const { info } = this.state;
    const { name } = this.props;
    return (
      <div>
        <p> 外部传入属性: {name} </p>
        <hr />
        <button onClick={this.changeInfo}> 修改状态 </button>
        <p> {info} </p>
      </div>
    );
  }
  componentDidUpdate() {
    console.log("componentDidUpdate");
    //* 组件更新结束
    //* 他的作用就是可以进行DOM操作
    //* 这个钩子函数中不能使用 this.setState(),否则会造成栈溢出
    document.querySelector("p").style.background = "red";
  }
}

//17 版本

import React, { Component } from "react";

export default class lifeCompRunning17 extends Component {
  // ? 未来版本的运行中生命周期钩子函数
  constructor(props) {
    super(props);

    this.state = {
      age: 18,
      username: "李大傻子"
    };
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("张浩雨:  -> prevState", prevState);
    console.log("张浩雨:  -> nextProps", nextProps);
    //? 可以用来监听当前组件的props 的变化情况
    console.log("getDerivedStateFromProps");
    return {
      age: 24,
      username: nextProps.name
    };
  }
  shouldComponentUpdate() {
    console.log("shouldComponentUpdate");
    return true;
  }

  render() {
    console.log("render");
    const { name } = this.props;
    const { age, username } = this.state;
    return (
      <div>
        {username}
        hello {age}岁的{name}
      </div>
    );
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    /* 给componentDidUpdate 一个返回值 */
    return "2000";
  }
  componentDidUpdate(a, b, c) {
    console.log("componentDidUpdate");
    console.log("兵哥: LifeCompRunning -> componentDidUpdate -> c", c); // c是getSnapshotBeforeUpdate的返回值
    console.log("兵哥: LifeCompRunning -> componentDidUpdate -> b", b); // b是更新后的新状态
    console.log("兵哥: LifeCompRunning -> componentDidUpdate -> a", a); // 变化前的属性
  }
}
三、错误处理阶段
import React, { Component, logErrorToMyService } from "react";

export default class ErrorComp extends Component {
  constructor(props) {
    super(props);

    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError() {
    //? 未来版本的错误捕获
    //*  更新 state 使下一次渲染能够显示降级后的 UI
    return {
      hasError: false
    };
  }

  componentDidCatch(error, info) {
    //? 现在版本的错误捕获
    //*  你同样可以将错误日志上报给服务器
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong</h2>;
    }
    return this.props.children;
  }
}
//使用
import React, { Component } from "react";
import "./App.css";
import { withAnimateHOC } from "./hoc";
import Home from "./pages/Home";
import ErrorComp from "./components/ErrorComp";

const HomeHOC = withAnimateHOC(Home);

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      flag: true,
      timeout: 200,
      classNames: {
        enter: "animated",
        enterActive: "slideInLeft",
        exit: "animated",
        exitActive: "slideOutRight"
      }
    };
  }

  changeFlag = () => {
    const { flag } = this.state;
    this.setState({
      flag: !flag
    });
  };
  render() {
    const { flag, timeout, classNames } = this.state;
    return (
      <div className="App">
        <h3> 生命周期最后一个阶段 - 错误处理阶段 </h3>
        <button onClick={this.changeFlag}>changeFlag</button>
        <ErrorComp>
          {" "}
          //* 定位
          <HomeHOC
            flag={flag}
            timeout={timeout}
            classNames={classNames}
          ></HomeHOC>
        </ErrorComp>
      </div>
    );
  }
}

各生命周期详解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81rkTOxU-1571741286143)(https://ftp.bmp.ovh/imgs/2019/10/c284a41f2a42840c.png)]

1. constructor(props)

React 组件的构造函数在挂载之前被调用。在实现React.Component构造函数时,需要先在添加其他内容前,调用super(props),用来将父组件传来的props绑定到这个类中,使用this.props将会得到。

白话: 继承父组件外部传递过来的属性,然后绑定到 当前 组件的 props 属性身上

this.props = props

官方建议不要在`constructor`引入任何具有副作用和订阅功能的代码,这些应当使用`componentDidMount()`。

constructor中应当做些初始化的动作,如:初始化state,将事件处理函数绑定到类实例上,但也不要使用setState()。如果没有必要初始化 state 或绑定方法,则不需要构造constructor,或者把这个组件换成纯函数写法。

  1. 定义状态
  2. 方法绑定 this

this.fn = this.fn.bind( this )

当然也可以利用props初始化state,在之后修改state不会对props造成任何修改,但仍然建议大家提升状态到父组件中,或使用redux统一进行状态管理。

constructor(props) {
  super(props);
  this.state = {
    isLiked: props.isLiked
  };
}
2. static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps` 是 react16.3 之后新增

  • 在组件实例化后,和接受新的props后被调用。
  • 他必须返回一个对象来更新状态,或者返回 null 表示新的 props 不需要任何 state 的更新。
  • 如果是由于父组件的props更改,所带来的重新渲染,也会触发此方法。
  • 调用steState()不会触发getDerivedStateFromProps()
  • 之前这里都是使用constructor+componentWillRecieveProps完成相同的功能的
static async getDerivedStateFromProps ( nextProps, preState ) {
  console.log("张浩雨: getDerivedStateFromProps -> preState", preState)
  console.log("张浩雨: getDerivedStateFromProps -> nextProps", nextProps)
  //*  nextProps 表示变化后的新属性  prevState表示先前的状态
  //*  在组件实例化后,和接受新的props后被调用。他必须返回一个对象来更新状态
  //*  这个钩子函数不能写this.setState()
  const result = await request( {
      url: "/mock/index.json"
  } );
  console.log("张浩雨: LifeCompInistial17 -> getDerivedStateFromProps -> result", result)
  return {   //? 更新状态的
      username: result.name
  }
}
3. componentWillMount() / UNSAFE_componentWillMount()

UNSAFE 前缀表示当前这个 api 已经过时了
componentWillMount()将在React未来版本(官方说法 17.0)中被弃用。 为了避免副作用和其他的订阅,官方都建议使用componentDidMount()`代替。这个方法是用于在服务器渲染上的唯一方法。这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改 state 的地方。

4. render()

render()方法是必需的。当他被调用时,他将计算this.propsthis.state,并返回以下一种类型:

  1. React 元素。通过 jsx 创建,既可以是 dom 元素,也可以是用户自定义的组件。
  2. 字符串或数字。他们将会以文本节点形式渲染到 dom 中。
  3. Portals【'portl】。react 16 版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在 DOM 树的任何位置。
  4. null,什么也不渲染
  5. 布尔值。也是什么都不渲染。

当返回null,false,ReactDOM.findDOMNode(this)将会返回 null,什么都不会渲染。

render()方法必须是一个纯函数,他不应该改变state,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
如果shouldComponentUpdate()返回falserender()不会被调用。

5. componentDidMount

componentDidMount在组件被装配后立即调用。初始化使得 DOM 节点应该进行到这里。

通常在这里进行 ajax 请求

如果要初始化第三方的 dom 库,也在这里进行初始化。只有到这里才能获取到真实的 dom.

6.componentWillReceiveProps()/UNSAFE_componentWillReceiveProps( nextProps )

官方建议使用getDerivedStateFromProps函数代替componentWillReceiveProps
当组件挂载后,接收到新的props后会被调用。如果需要更新state来响应props的更改,则可以进行this.propsnextProps的比较,并在此方法中使用this.setState() > 如果父组件会让这个组件重新渲染,即使props没有改变,也会调用这个方法。
React 不会在组件初始化 props 时调用这个方法。调用this.setState也不会触发。

####7.shouldComponentUpdate(nextProps, nextState)
调用shouldComponentUpdate使 React 知道,组件的输出是否受stateprops的影响。默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为。
在渲染新的propsstate前,shouldComponentUpdate会被调用。默认为true。这个方法不会在初始化时被调用,也不会在forceUpdate()时被调用。

如果shouldComponentUpdate()返回falsecomponentWillUpdate,rendercomponentDidUpdate不会被调用。
官方并不建议在shouldComponentUpdate()中进行深度查询或使用JSON.stringify(),他效率非常低,并且损伤性能。

8. componentWillUpdate/UNSAFE*componentWillUpdate(nextProps, nextState)

在渲染新的stateprops时,UNSAFE_componentWillUpdate会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。 *不能在这里使用 this.setState()_,也不能做会触发视图更新的操作。如果需要更新stateprops,调用getDerivedStateFromProps

9 getSnapshotBeforeUpdate()

在 react render()后的输出被渲染到 DOM 之前被调用。
它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。
这个生命周期返回的任何值都将作为参数传递给 componentDidUpdate()。

10) componentDidUpdate(prevProps, prevState, snapshot)
在更新发生后立即调用`componentDidUpdate()`。此方法不用于初始渲染。当组件更新时,将此作为一个机会来操作 DOM。只要您将当前的 props 与以前的 props 进行比较(例如,如果 props 没有改变,则可能不需要网络请求),这也是做网络请求的好地方。

如果组件实现getSnapshotBeforeUpdate()生命周期,则它返回的值将作为第三个“快照”参数传递给componentDidUpdate()。否则,这个参数是undefined

11. componentWillUnmount()

在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效,取消网络请求或清理在componentDidMount中创建的任何监听。

12. componentDidCatch(error, info)

错误边界是 React 组件,可以在其子组件树中的任何位置捕获 JavaScript 错误记录这些错误并显示回退 UI,而不是崩溃的组件树。
错误边界在渲染期间生命周期方法以及整个树下的构造函数中捕获错误。

如果类组件定义了此生命周期方法 12.componentDidCatch,则它将成错误边界。在它中调用setState()可以让你在下面的树中捕获未处理的 JavaScript 错误,并显示一个后备 UI。只能使用错误边界从意外异常中恢复; 不要试图将它们用于控制流程。

错误边界只会捕获树中下面组件中的错误。错误边界本身不能捕获错误。

13. static getDerivedStateFromError(){ //? 未来版本的错误捕获

//* 更新 state 使下一次渲染能够显示降级后的 UI

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值