01react基础-04组件生命周期

98 阅读6分钟

生命周期

概念

1. 生命周期-概览

了解react类组件生命周期整体情况

大致步骤:

  • 什么是生命周期
  • React类组件的生命周期整体概览
  • 了解生命周期的意义

具体内容:

  1. 什么是生命周期
    • 一个事物从创建到最后消亡经历的整个过程

人一生缩影

  1. React类组件的生命周期整体概览,组件从创建到消耗的过程

React组件生命周期

img

  1. 了解生命周期的意义
    • 有助于理解组件的运行方式、完成更复杂的组件功能、分析组件错误原因
    • 钩子函数为开发人员在不同阶段操作组件提供了时机

总结:

  • 只有类组件才有生命周期,分为 挂载阶段 更新阶段 卸载阶段

阶段

2. 生命周期-挂载阶段

够说出组件的挂载阶段的钩子函数以及执行时机

大致步骤:

  • 知道挂载阶段会执行那些函数,执行顺序
  • 知道每个函数内一般可以做什么事
  • 参考代码

具体内容:

  1. 知道挂载阶段会执行那些函数,执行顺序

img

  1. 知道每个函数内一般可以做什么事
钩子 函数触发时机作用
constructor创建组件时,最先执行1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
render每次组件渲染都会触发渲染UI(注意: 不能调用setState()
componentDidMount组件挂载(完成DOM渲染)后1. 发送网络请求 2.DOM操作
  1. 参考代码
import { Component } from 'react'


export default class App extends Component {
  constructor () {
    super()
    console.log('1. constructor执行')
  }
  componentDidMount () {
    console.log('3. componentDidMount执行')
  }
  render() {
    console.log('2. render执行')
    return <div>App组件</div>
  }
}

总结:

  • 组件挂载阶段,顺序执行 constructor render componentDidMount 三个函数

3. 生命周期-更新阶段

能够说出组件的更新阶段的钩子函数以及执行时机

大致步骤:

  • 知道更新阶段会执行那些函数,执行顺序
  • 知道何时触发更新阶段
  • 知道触发的钩子函数里可以做些什么
  • 参考代码

具体内容:

  1. 更新阶段会执行那些函数,执行顺序

img

  1. 何时触发更新阶段
    1. setState()
    2. forceUpdate() 强制组件更新
    3. 组件接收到新的props(实际上,只需要父组件更新,子组件就会重新渲染)
  2. 钩子函数里可以做什么
钩子函数触发时机作用
render每次组件渲染都会触发渲染UI(与 挂载阶段 是同一个render)
componentDidUpdate组件更新(完成DOM渲染)后DOM操作,可以获取到更新后的DOM内容,不要直接调用setState
  1. 参考代码
import { Component } from 'react'


class Child extends Component {
  render() {
    return <h1>统计豆豆被打的次数:</h1>
  }
}


export default class App extends Component {
  state = {
    count: 0
  }
	
	handleClick = () => {
    this.setState({
      count: this.state.count + 1
    })
  }


  componentDidUpdate() {
    console.log('2. componentDidUpdate执行')
  }


  render() {
    console.log('1. render执行')
    return (
      <div>
        <Child />
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    )
  }
}

总结:

  • 组件更新会触发 componentDidUpdate 钩子函数

4. 生命周期-卸载阶段

能够说出组件的销毁阶段的钩子函数以及执行时机

大致步骤:

  • 什么时候触发卸载
  • 卸载阶段执行那些钩子函数,一般做什么事情
  • 参考代码
  • 演示清理工作

具体内容:

  1. 什么时候触发卸载?
    • 在组件被移除的时候(消失)触发卸载阶段
  2. 卸载阶段执行那些钩子函数,一般做什么事情
钩子函数触发时机作用
componentWillUnmount组件卸载(从页面中消失)执行清理工作(比如:清理定时器等)
  1. 参考代码
import { Component } from 'react'


class Child extends Component {
  componentWillUnmount () {
    console.log('componentWillUnmount执行')
  }
  render() {
    return <h1>统计豆豆被打的次数:{this.props.count}</h1>
  }
}


export default class App extends Component {
  state = {
    count: 0
  }
	
	handleClick = () => {
    this.setState({
      count: this.state.count + 1
    })
  }


  render() {
    return (
      <div>
        { this.state.count < 5 && <Child count={this.state.count} />}
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    )
  }
}

总结:

  • 组件卸载阶段执行 componentWillUnmount, 可以清理全局事件、定时器等。

使用

5. 生命周期 - 练习

能够实现youku评论列表案例的数据持久化

大致步骤:

  1. 在组件挂载的钩子函数中,从本地存储中读取 list 数据
  2. 将读取到的 list 数据,更新到状态中
  3. 在组件更新的钩子函数中,将最新的 list 数据存到本地存储中

核心代码:

componentDidUpdate() {
  localStorage.setItem('comments', JSON.stringify(this.state.comments))
}


componentDidMount() {
  const comments = JSON.parse(localStorage.getItem('comments') || '[]')
  this.setState({ comments })
}

总结: 掌握生命周期的基本运用

todomvc案例优化

模拟接口

13. todomvc案例-模拟接口

使用json-server创建接口服务支持案例

大致步骤:

  • 全局安装json-server
  • 新建 db.json 文件
  • 启动接口服务
  • 接口地址列表

具体内容:

  1. 全局安装json-server
npm i json-server -g
  1. 新建 db.json 文件,内容如下
{
  "todos": [
    {"id":1,"name":"吃饭","done":true},
    {"id":2,"name":"睡觉","done":false}
  ]
}
  1. 启动服务,在db.json文件目录下执行
json-server db.json
# 设置端口
# json-server db.json --port 5000
  1. 接口地址列表
GET /todos 获取列表
POST /todos 添加任务,参数 name 和 done
GET /todos/:id 获取任务,参数 id
DELETE /todos/:id 删除任务,参数 id
PUT /todos/:id 修改任务,参数 name 和 done , 完整更新
PATCH /todos/:id 修改任务,参数 name 或 done , 局部更新

功能

14. todomvc案例-静态结构

根据todomvc提供的资源搭建静态案例

TODOMVC官网:todomvc.com/

todo案例基础模版地址: todo-app-template

将基础模版加入到react脚手架中

react组件准备:

  • 创建 index.css 把css拷贝进去
  • 创建 App.js 组件,把html使用起来
import React, { Component } from 'react';
import './index.css'
export default class App extends Component {
  render() {
    return (
      <section className="todoapp">
        <header className="header">
          <h1>todos</h1>
          <input
            className="new-todo"
            placeholder="What needs to be done?"
            autoFocus
          />
        </header>
        <section className="main">
          <input id="toggle-all" className="toggle-all" type="checkbox" />
          <label htmlFor="toggle-all">Mark all as complete</label>
          <ul className="todo-list">
            <li className="completed">
              <div className="view">
                <input className="toggle" type="checkbox" checked />
                <label>Taste JavaScript</label>
                <button className="destroy"></button>
              </div>
              <input className="edit" value="Create a TodoMVC template" />
            </li>
            <li>
              <div className="view">
                <input className="toggle" type="checkbox" />
                <label>Buy a unicorn</label>
                <button className="destroy"></button>
              </div>
              <input className="edit" value="Rule the web" />
            </li>
          </ul>
        </section>
        <footer className="footer">
          <span className="todo-count">
            <strong>0</strong> item left
          </span>
          <ul className="filters">
            <li>
              <a className="selected" href="#/">All</a>
            </li>
            <li>
              <a href="#/active">Active</a>
            </li>
            <li>
              <a href="#/completed">Completed</a>
            </li>
          </ul>
          <button className="clear-completed">Clear completed</button>
        </footer>
      </section>
    );
  }
}

15. todomvc案例-列表渲染

完成组件的拆分和列表渲染

大致步骤:

  • 拆分组件
  • 安装axios,在App组件获取数据,传人Main渲染即可

具体代码:

  1. 拆分组件
App.jsx
import React, { Component } from "react";
import Footer from "./components/Footer";
import Header from "./components/Header";
import Main from "./components/Main";


export default class App extends Component {
  render() {
    return (
      <section className="todoapp">
        <Header />
        <Main />
        <Footer />
      </section>
    );
  }
}
components/Header.jsx
import React, { Component } from "react";


export default class Header extends Component {
  render() {
    return (
      <header className="header">
        <h1>todos</h1>
        <input
          className="new-todo"
          placeholder="What needs to be done?"
          autoFocus
        />
      </header>
    );
  }
}
components/Main.jsx
const Main = () => {
  return (
    <section className="main">
      <input id="toggle-all" className="toggle-all" type="checkbox" />
      <label htmlFor="toggle-all">Mark all as complete</label>
      <ul className="todo-list">
        <li className="completed">
          <div className="view">
            <input className="toggle" type="checkbox" checked />
            <label>Taste JavaScript</label>
            <button className="destroy"></button>
          </div>
          <input className="edit" value="Create a TodoMVC template" />
        </li>
        <li>
          <div className="view">
            <input className="toggle" type="checkbox" />
            <label>Buy a unicorn</label>
            <button className="destroy"></button>
          </div>
          <input className="edit" value="Rule the web" />
        </li>
      </ul>
    </section>
  );
};


export default Main;
components/Footer.jsx
const Footer = () => {
  return (
    <footer className="footer">
      <span className="todo-count">
        <strong>0</strong> item left
      </span>
      <ul className="filters">
        <li>
          <a className="selected" href="#/">
            All
          </a>
        </li>
        <li>
          <a href="#/active">Active</a>
        </li>
        <li>
          <a href="#/completed">Completed</a>
        </li>
      </ul>
      <button className="clear-completed">Clear completed</button>
    </footer>
  );
};


export default Footer;
  1. 安装axios,在App组件获取数据,传人Main渲染即可
App.jsx
import React, { Component } from "react";
import Footer from "./components/Footer";
import Header from "./components/Header";
import Main from "./components/Main";
import axios from "axios";


export default class App extends Component {
  state = {
    list: [],
  };
  async componentDidMount() {
    const res = await axios.get("http://localhost:5000/todos");
    this.setState({ list: res.data });
  }
  render() {
    return (
      <section className="todoapp">
        <Header />
        <Main list={this.state.list} />
        <Footer />
      </section>
    );
  }
}
components/Main.jsx
import PropTypes from 'prop-types'
const Main = (props) => {
  return (
    <section className="main">
      <input id="toggle-all" className="toggle-all" type="checkbox" />
      <label htmlFor="toggle-all">Mark all as complete</label>
      <ul className="todo-list">
        {props.list.map((item) => (
          <li key={item.id} className={item.done ? "completed" : ""}>
            <div className="view">
              <input className="toggle" type="checkbox" checked={item.done} onChange={()=>{}} />
              <label>{item.name}</label>
              <button className="destroy"></button>
            </div>
            <input className="edit"/>
          </li>
        ))}
      </ul>
    </section>
  );
};
Main.propTypes = {
  list: PropTypes.array
}


export default Main;

16. todomvc案例-添加任务

完成添加任务功能

大致步骤:

  • Header组件
    • 绑定输入内容数据
    • enter后拿着内容,发送添加请求
    • 清空输入内容
    • 更新列表
  • App组件
    • 提供更新列表函数,传递给Header组件使用

落地代码:

App组件
componentDidMount() {
    this.getTodoList()
  }
  getTodoList = async () => {
    const res = await axios.get("http://localhost:5000/todos");
    this.setState({ list: res.data });
  }
  render() {
    return (
      <section className="todoapp">
        <Header getTodoList={this.getTodoList} />
        <Main list={this.state.list} />
        <Footer />
      </section>
    );
  }
Header组件
import React, { Component } from "react";
import axios from "axios";
import PropTypes from 'prop-types'


export default class Header extends Component {


  static propTypes = {
    getTodoList: PropTypes.func.isRequired
  }


  state = {
    todoName: "",
  };


  onChange = (e) => {
    this.setState({
      todoName: e.target.value,
    });
  };


  add = async (e) => {
    if (e.keyCode === 13) {
      const name = this.state.todoName.trim();
      if (name) {
        await axios.post("http://localhost:5000/todos", { name, done: false });
        this.setState({ todoName: "" });
        this.props.getTodoList();
      }
    }
  };


  render() {
    return (
      <header className="header">
        <h1>todos</h1>
        <input
          className="new-todo"
          placeholder="What needs to be done?"
          autoFocus
          value={this.state.todoName}
          onChange={this.onChange}
          onKeyUp={this.add}
        />
      </header>
    );
  }
}

17. todomvc案例-修改状态&删除任务

完成修改状态功能&删除任务

大致步骤:

  • Main组件
    • 绑定checkbox的onChang事件,触发后发修改请求
    • 绑定button的点击事件,触发后发删除请求
    • 成功后更新列表
  • App组件
    • 传入更新列表函数

落地代码:Main组件

import PropTypes from "prop-types";
+import axios from "axios";
const Main = (props) => {
+  const updateDone = async (id, done) => {
+    await axios.patch(`http://localhost:5000/todos/${id}`, { done });
+    props.getTodoList()
+  };
+  const del = async (id) => {
+    await axios.delete(`http://localhost:5000/todos/${id}`);
+    props.getTodoList()
+  }
  return (
    <section className="main">
      <input id="toggle-all" className="toggle-all" type="checkbox" />
      <label htmlFor="toggle-all">Mark all as complete</label>
      <ul className="todo-list">
        {props.list.map((item) => (
          <li key={item.id} className={item.done ? "completed" : ""}>
            <div className="view">
              <input
                className="toggle"
                type="checkbox"
                checked={item.done}
+               onChange={() => updateDone(item.id, !item.done)}
              />
              <label>{item.name}</label>
+              <button className="destroy" onClick={()=>del(item.id)}></button>
            </div>
            <input className="edit" />
          </li>
        ))}
      </ul>
    </section>
  );
};
Main.propTypes = {
  list: PropTypes.array.isRequired,
+  getTodoList: PropTypes.func.isRequired,
};


export default Main;
App组件
<section className="todoapp">
        <Header getTodoList={this.getTodoList} />
+        <Main list={this.state.list} getTodoList={this.getTodoList} />
        <Footer />
      </section>