React 组件生命周期

组件挂载阶段

  • 组件被创建然后插入 DOM 中
  • 生命周期方法
    • constructor 设置组件的初始配置
    • render 解析 JSX,在界面上展示
    • componentDidMount 组件挂载完毕调用
      • 发送网络请求
      • 添加定时器
      • 添加事件监听
      • 获取DOM元素 ```jsx import React, { Component } from ‘react’

class App extends Component { constructor () { super()

  1. // 初始化状态
  2. this.state = {
  3. count: 0
  4. }
  5. // 改变 this 指向
  6. this.handler = this.handler.bind(this)

}

handler() { console.log(this) } // 解析JSX render () { console.log(‘render执行了’) return (

组件挂载

{this.state.count}
) } // 挂载完毕 componentDidMount() { console.log(‘componentDidMount执行了, 挂载完成’) setInterval(() => { this.setState({ count: this.state.count + 1 }) },1000) } }

export default App

  1. <a name="O1Qor"></a>
  2. ### 组件更新阶段
  3. - 当数据更新之后,组件需要被重新渲染
  4. - 外部传入的 props,以及自身管理的状态
  5. - 生命周期方法
  6. - shouldComponentUpdate(nextProps, nextState)
  7. - 返回一个布尔值,决定组件是否更新,默认返回true
  8. - 如果次方法返回 false 那么后续的方法不会在执行
  9. - render 解析JSX,渲染DOM
  10. - componentDidUpdate 组件更新完成之后执行
  11. ```jsx
  12. // App.js
  13. import React, { Component } from 'react'
  14. import About from './About'
  15. class App extends Component {
  16. constructor () {
  17. super()
  18. // 初始化状态
  19. this.state = {
  20. count: 0
  21. }
  22. // 改变 this 指向
  23. this.handler = this.handler.bind(this)
  24. }
  25. handler() {
  26. console.log(this)
  27. this.setState({
  28. count: this.state.count + 1
  29. })
  30. }
  31. // 解析JSX
  32. render () {
  33. console.log('render执行了')
  34. return (
  35. <div>
  36. <h1>组件组件更新</h1>
  37. <div>{this.state.count}</div>
  38. <button onClick={this.handler}>点击</button>
  39. <About />
  40. </div>
  41. )
  42. }
  43. // 挂载完毕
  44. componentDidMount() {
  45. console.log('componentDidMount执行了, 挂载完成')
  46. // setInterval(() => {
  47. // this.setState({
  48. // count: this.state.count + 1
  49. // })
  50. // },1000)
  51. }
  52. // 数据更新触发
  53. shouldComponentUpdate(nextProps, nextState) {
  54. console.log('shouldComponentUpdate执行了')
  55. console.log(nextProps, nextState)
  56. return true
  57. }
  58. }
  59. export default App
  1. import React, { Component, PureComponent } from 'react'
  2. //PureComponent 该组件的数据更新 才重新更新
  3. class About extends PureComponent {
  4. constructor() {
  5. super()
  6. this.state = {
  7. count: 10
  8. }
  9. }
  10. handler = () => {
  11. this.setState({
  12. count: this.state.count + 5
  13. })
  14. }
  15. render() {
  16. console.log('About组件执行了 render')
  17. return (
  18. <div>
  19. <hr />
  20. <h2>About组件内容</h2>
  21. <div>{this.state.count}</div>
  22. <button onClick={this.handler}>点击</button>
  23. </div>
  24. );
  25. }
  26. // shouldComponentUpdate(nextProps, nextState) {
  27. // // 数据更新才重新渲染界面 已不被推荐使用
  28. // if (nextState.count === this.state.count) return false
  29. // return true
  30. // }
  31. }
  32. export default About

组件卸载阶段

  • 将组件从 DOM 上删除
  • 生命周期方法
    • componentWillUnmount ```jsx // App.js import React, { Component } from ‘react’ import About from ‘./About’

class App extends Component { constructor () { super() // 初始化状态 this.state = { isShow: true } }

// 解析JSX render () { console.log(‘render执行了’) return (

组件销毁

{this.state.isShow && }
) }

}

export default App

  1. ```jsx
  2. // Abput.js
  3. import React, { PureComponent } from 'react'
  4. //PureComponent 该组件的数据更新 才重新更新
  5. class About extends PureComponent {
  6. render() {
  7. return (
  8. <div>
  9. <hr />
  10. <h2>About组件内容</h2>
  11. </div>
  12. );
  13. }
  14. foo = () => {
  15. console.log('about组件中的click操作触发')
  16. }
  17. componentDidMount () {
  18. // 当 about 组件挂载完毕就可以执行 DOM 相关操作
  19. //添加事件监听
  20. window.addEventListener('click', this.foo)
  21. }
  22. // 组件卸载之前执行
  23. componentWillUnmount(){
  24. window.removeEventListener('click', this.foo)
  25. }
  26. }
  27. export default About

React 发送 ajax请求

  1. import React, { Component } from "react";
  2. import axios from "axios";
  3. class App extends Component {
  4. constructor() {
  5. super();
  6. this.state = {
  7. msg: "",
  8. };
  9. }
  10. render() {
  11. return <div>当前数据:{this.state.msg}</div>;
  12. }
  13. async componentDidMount() {
  14. const data = await axios
  15. .get("http://localhost:2021/api/welcome")
  16. .then((res) => res.data);
  17. this.setState(data);
  18. }
  19. }
  20. export default App;

React 请求转发

  • 数据会存在不同的服务器
  • 服务器与服务端是不存在跨域问题
  • 客户端应用发请求给同源的服务端,服务端将请求转发给 API 服务器端, API 服务器将数据处理之后返回给… 客户端应用

截屏2021-04-11 下午9.35.01.png

方式一

  • package.json 配置需要请求的服务器地址

    1. "proxy": "http://localhost:2021"
  • App.js

    1. async componentDidMount() {
    2. const data = await axios
    3. .get("/api/welcome")
    4. .then((res) => res.data);
    5. this.setState(data);
    6. }

方式二

module.exports = app => { app.use(‘/api’, createProxyMiddleware({ target: ‘http://loaclhost:2021‘, changeOrigin: true })) }

  1. - 发送请求
  2. ```jsx
  3. async componentDidMount() {
  4. const data = await axios
  5. .get("/api/welcome")
  6. .then((res) => res.data);
  7. this.setState(data);
  8. }

React中 mock 数据

  • public/api/article.json

    1. [
    2. {
    3. "id": 1,
    4. "title": "文章一"
    5. },
    6. {
    7. "id": 2,
    8. "title": "文章二"
    9. }
    10. ]
  • 请求数据 ```jsx import React, { Component } from “react”; import axios from “axios”;

class App extends Component {

constructor() { super(); this.state = { articles: [] } }

getArticle = async () => { const articles = await axios.get(‘api/article.json’).then(response => response.data)

this.setState({articles}) } render() { return (

    { this.state.articles.map(article => (
  • {article.title}
  • )) }
) }

}

export default App;

  1. <a name="7DMmG"></a>
  2. ## Redux
  3. - Redux 是一个数据管理框架,提供了一个 store 的统一数据存储仓库
  4. - store 就像是 一个数据管理 中间人,让组件之间无需再直接进行数据传递
  5. <a name="02sQT"></a>
  6. ### 工作流程
  7. ![截屏2021-04-11 下午10.21.35.png](https://cdn.nlark.com/yuque/0/2021/png/1670748/1618150981138-8c89da7e-6d73-4596-bd5a-664cde268ca1.png#height=800&id=QdUF2&margin=%5Bobject%20Object%5D&name=%E6%88%AA%E5%B1%8F2021-04-11%20%E4%B8%8B%E5%8D%8810.21.35.png&originHeight=800&originWidth=2375&originalType=binary&size=305860&status=done&style=none&width=2375)
  8. - Store
  9. - 对象类型
  10. - 负责存储数据,更新视图
  11. - Action Creators
  12. - 对象类型
  13. - 存储数据的操作行为
  14. - Reducers
  15. - 函数类型
  16. - 接收 Action 行为,处理数据,将处理之后的数据返回给 Store
  17. <a name="QDInA"></a>
  18. ### 创建 store及 reducer
  19. - 安装依赖包 `npm install redux react-redux -D`
  20. - src/index.js
  21. ```jsx
  22. import { createStore } from 'redux'
  23. import CounterReducer from './Store/Reducer/Counter.reducer'
  24. const store = createStore(CounterReducer)
  • src/Store/Reducer/Counter.reducer.js
    1. export default () => {
    2. return {
    3. count: 0
    4. }
    5. }

获取 store

  • src/index.js ```jsx

import { createStore } from ‘redux’ import CounterReducer from ‘./Store/Reducer/Counter.reducer’

import { Provider } from ‘react-redux’

const store = createStore(CounterReducer)

ReactDOM.render( , document.getElementById(‘root’) );

  1. - src/Components/Counter.js
  2. ```jsx
  3. import React, { Component } from 'react'
  4. import {connect} from 'react-redux'
  5. function Counter (props) {
  6. return (
  7. <div>
  8. <button>+1</button>
  9. <div>{props.count}</div>
  10. <button>-1</button>
  11. </div>
  12. )
  13. }
  14. const mapStateToProps = state => ({
  15. count: state.count
  16. })
  17. // 第一次调用指明传递什么数据
  18. // 第二次调用指明传递给哪个组件
  19. export default connect(mapStateToProps)(Counter)

组件修改 store 数据

  • src/Components/Counter.js

    1. function Counter (props) {
    2. return (
    3. <div>
    4. <button onClick={() => {props.dispatch({type: 'increment'})}}>+1</button>
    5. <div>{props.count}</div>
    6. <button onClick={() => {props.dispatch({type: 'decrement'})}}>-1</button>
    7. </div>
    8. )
    9. }
  • src/Store/Reducer/Counter.reducer.js ```jsx const initialState = { count: 0 }

export default (state=initialState, action) => { console.log(action) switch (action.type) { case ‘increment’ : return { count: state.count + 1 } case ‘decrement’ : return { count: state.count - 1 } default : return state } return state }

  1. <a name="mfkdG"></a>
  2. ### action 传递参数
  3. - src/Components/Counter.js
  4. ```jsx
  5. <button onClick={() => {props.dispatch({type: 'increment_n', payload: 5})}}>+5</button>
  • src/Store/Reducer/Counter.reducer.js
    1. case 'increment_n' :
    2. return {
    3. count: state.count + action.payload
    4. }

提取 action 代码为函数

  • src/Components/Counter.js ```jsx import React from ‘react’ import {connect} from ‘react-redux’

function Counter (props) { return (

{props.count}
) }

const mapStateToProps = state => ({ count: state.count })

const mapDispatchToProps = dispatch => ({ increment() { dispatch({type: ‘increment’}) }, increment_n(payload){ dispatch({type: ‘increment_n’, payload}) }, decrement() { dispatch({type: ‘decrement’}) } })

// 第一次调用指明传递什么数据 // 第二次调用指明传递给哪个组件 export default connect(mapStateToProps, mapDispatchToProps)(Counter)

  1. <a name="t2jnC"></a>
  2. ### 自动生成 action 触发函数
  3. - src/Store/Actions/Counter.actions.js
  4. ```jsx
  5. export const increment = () => ({type: 'increment'})
  6. export const decrement = () => ({type: 'decrement'})
  7. export const increment_n = (payload) => ({type: 'increment_n', payload})
  • src/Components/Counter.js ```jsx import React from ‘react’ import {connect} from ‘react-redux’

import { bindActionCreators } from ‘redux’ import * as counterActions from ‘../Store/Actions/Counter.actions’

function Counter (props) { return (

{props.count}
) }

const mapStateToProps = state => ({ count: state.count })

const mapDispatchToProps = dispatch => ({ …bindActionCreators(counterActions,dispatch) })

// 第一次调用指明传递什么数据 // 第二次调用指明传递给哪个组件 export default connect(mapStateToProps, mapDispatchToProps)(Counter)

  1. <a name="lOk7T"></a>
  2. ### 设置 action 类型常量
  3. - Store/Action_types/Counter.actions.types.js
  4. ```javascript
  5. export const INCREMENT = 'increment'
  6. export const DECREMENT = 'decrement'
  7. export const INCREMENT_N = 'increment_n'
  • src/Store/Actions/Counter.actions.js ```javascript import { DECREMENT, INCREMENT, INCREMENT_N } from “../Action_types/Counter.actions.types”

export const increment = () => ({type: INCREMENT}) export const decrement = () => ({type: DECREMENT}) export const increment_n = (payload) => ({type: INCREMENT_N, payload})

  1. - src/Store/Reducer/Counter.reducer.js
  2. ```javascript
  3. import { DECREMENT, INCREMENT, INCREMENT_N } from "../Action_types/Counter.actions.types"
  4. const initialState = {
  5. count: 0
  6. }
  7. export default (state = initialState, action) => {
  8. console.log(action)
  9. switch (action.type) {
  10. case INCREMENT :
  11. return {
  12. count: state.count + 1
  13. }
  14. case DECREMENT :
  15. return {
  16. count: state.count - 1
  17. }
  18. case INCREMENT_N:
  19. return {
  20. count: state.count + action.payload
  21. }
  22. default :
  23. return state
  24. }
  25. return state
  26. }

reducer 拆分与合并

  • src/Reducer

    • Counter.reducer.js
    • Person.reducer.js
    • index.js ```javascript import {combineReducers} from ‘redux’ import counterReducer from ‘./Counter.reducer’ import personReducer from ‘./Person.reducer’

    export default combineReducers({ counter: counterReducer, person: personReducer }) ```

redux 工作流程

  • 创建 store 管理 reducer ```javascript import { createStore } from ‘redux’

// 3 定义初始数据 const initialState = { content: [‘默认数据’] }

// 2 创建 reducer function reducer (state = initialState, action) { // state: reducer 当中处理完数据之后返回给 store 进行存储的数据 // action: store 传递给 reducer 的具体指令 switch(action.type) { case ‘addContent’: return { content: [ …state.content, action.content ] } default: return state } }

// 1 创建 store 存储数据 const store = createStore(reducer)

  1. - 利用 provider store 向后传递
  2. ```jsx
  3. import { Provider } from 'react-redux'
  4. ReactDOM.render(
  5. <React.StrictMode>
  6. <Provider store={store}><App /></Provider>
  7. </React.StrictMode>,
  8. document.getElementById('root')
  9. );
  • 在具体的组件当中使用 content 方法获取 store 里保存的数据,通过组件 的 Props 进行访问 ```javascript import { connect } from ‘react-redux’

// 从 store 当中获取当前组件需要的数据 const mapStateToProps = state => ({ content: state.content })

export default connect(mapStateToProps)(App)

  1. - 渲染拿到的数据和修改
  2. ```jsx
  3. class App extends Component {
  4. constructor() {
  5. super()
  6. this.myRef = React.createRef()
  7. }
  8. handler = () => {
  9. // 获取 input 输入框输入内容
  10. const content = this.myRef.current.value
  11. // 调用 dispatch 方法将输入内容传给 action,在 reducer 当中进行处理
  12. this.props.dispatch({type: 'addContent',content})
  13. // 更新界面
  14. this.myRef.current.value = ''
  15. }
  16. render () {
  17. return (
  18. <div>
  19. <input type="text" placeholder="请输入标题" ref={this.myRef}/>
  20. <button onClick={this.handler}>新增</button>
  21. <hr />
  22. <ul>
  23. {
  24. this.props.content.map((item, index) => (
  25. <li key={index}>{item}</li>
  26. ))
  27. }
  28. </ul>
  29. </div>
  30. )
  31. }
  32. }

代码优化

  • 拆分合并所有的 reducer 交给 store 统一管理
  • 让 react 自动创建 action 执行的函数
  • 将 action 类型使用的字符串定义为常量后 需要使用就有提示

redux 中间键

React 基础2 - 图2

  1. import { createStore, applyMiddleware } from 'redux'
  2. function reducer(state, action) {
  3. console.log(action)
  4. return state
  5. }
  6. // 中间键函数
  7. function middle ({getState, dispatch}) {
  8. return function (next) {
  9. return function (action) {
  10. // 此处可以完成异步操作
  11. setTimeout(() => {
  12. //自定义数据
  13. action.payload = 100
  14. // 异步操作执行完成之后,需要将 action 交给 store 继续往后传递
  15. return next(action)
  16. },2000)
  17. }
  18. }
  19. }
  20. // 创建 store 存储数据
  21. const store = createStore(reducer, applyMiddleware(middle))
  22. store.dispatch({type: 'test'})

redux-thunk 异步解决方案

  • 安装插件 npm install redux-thunk
  • 注册中间件 ```jsx import { createStore, applyMiddleware } from ‘redux’

import thunk from ‘redux-thunk’

// 创建 store 存储数据 const store = createStore(totalReducer, applyMiddleware(thunk))

  1. - 使用中间件
  2. ```javascript
  3. import axios from 'axios'
  4. // export const getPersons = (payload) => ({type: 'getPersons', payload})
  5. export const getPersons = () => async (dispatch) => {
  6. let persons = await axios.get('http://localhost:2021/api/getUsers').then(response => response.data)
  7. dispatch({type: 'loadPersonSuccess', payload: persons})
  8. }

redux-saga 异步解决方案

  • 安装插件 npm install redux-saga -D
  • 注册中间件 src/index.js ```jsx import React from ‘react’; import ReactDOM from ‘react-dom’; import App from ‘./App’; import { createStore, applyMiddleware} from ‘redux’ import { Provider } from ‘react-redux’ import totalReducer from ‘./store/Reducers’ import createSagaMiddleware from ‘redux-saga’ import personSaga from ‘./store/Saga/person.saga’

const sagaMiddleware = createSagaMiddleware()

// 创建 store 存储数据 const store = createStore(totalReducer, applyMiddleware(sagaMiddleware)) sagaMiddleware.run(personSaga)

ReactDOM.render( , document.getElementById(‘root’) );

  1. - 书写中间件逻辑 src/store/Saga/person.saga.js
  2. ```javascript
  3. import {takeEvery, put} from 'redux-saga/effects'
  4. import axios from 'axios'
  5. function * loadPerson () {
  6. let persons = yield axios.get('http://localhost:2021/api/getUsers').then(response => response.data)
  7. yield put({type:'load_person_success', payload:persons})
  8. }
  9. export default function * personSaga() {
  10. yield takeEvery('load_person', loadPerson)
  11. }
  • reducer逻辑 src/store/Reducers/person.reducer.js ```javascript

// 定义初始数据 const initialState = { person: [] }

// 创建 reducer function reducer (state = initialState, action) { switch(action.type) { case ‘load_person_success’: return { person: action.payload } default: return state } }

export default reducer

  1. - action逻辑 src/store/Actions/person.actions.js
  2. ```javascript
  3. export const load_person = () => ({type: 'load_person'})
  • 使用数据 App.js ```jsx import React, { Component } from ‘react’ import { connect } from ‘react-redux’ import { bindActionCreators } from ‘redux’ import * as personActions from ‘./store/Actions/person.actions’

class App extends Component {

handler = () => { this.props.load_person() }

render () { console.log(this.props) return (

) } }

// 从 store 当中获取当前组件需要的数据 const mapStateToProps = state => ({ person: state.personReducer.person })

const mapDispatchToProps = (dispatch) => ({ …bindActionCreators(personActions, dispatch) })

export default connect(mapStateToProps, mapDispatchToProps)(App)

  1. <a name="zoLCu"></a>
  2. #### redux-saga 拆分与合并
  3. - saga/root.saga.js
  4. ```javascript
  5. import { all } from 'redux-saga/effects'
  6. import personSaga from './person.saga'
  7. export default function * rootSaga () {
  8. yield all([
  9. personSaga()
  10. ])
  11. }
  • index.js ```javascript import rootSaga from ‘./store/Saga/root.saga’

sagaMiddleware.run(rootSaga)

  1. <a name="AJVQT"></a>
  2. ### 简化 action与 reducer
  3. - 安装插件 `npm install redux-actions -D`
  4. - Store/Actions/counter.actions.js
  5. ```javascript
  6. import {createAction} from 'redux-actions'
  7. // export const increment = () => ({type: 'increment'})
  8. // export const decrement = () => ({type: 'decrement'})
  9. export const increment_action = createAction('increment')
  10. export const decrement_action = createAction('decrement')
  • Store/Reducers/counter.reducer.js ```javascript import { handleActions as createReducer} from ‘redux-actions’ import {increment_action, decrement_action} from ‘../Actions/counter.actions’ const initialState = { count: 0 }

const counterReducer = createReducer({ [increment_action]: (state, action) => ({count: state.count + 1}), [decrement_action]: (state, action) => ({count: state.count - 1}) }, initialState)

export default counterReducer ```