亲宝软件园·资讯

展开

React Redux使用

Rsquo 人气:0

一、理解JavaScript纯函数

1.1 纯函数的概念

纯函数的维基百科定义:

纯函数概念,总结如下:

案例(数组的两个方法):

1.2 副作用概念的理解

什么是副作用?

纯函数在执行的过程中就是不能产生这样的副作用:

1.3 纯函数在函数式编程的重要性

为什么纯函数在函数式编程中非常重要呢?

二、Redux的核心思想

2.1 为什么需要 Redux

JavaScript开发的应用程序变得越来越复杂:

管理不断变化的state是非常困难的:

React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:

2.2 Redux的核心概念

2.2.1 store

可以定义一些初始化的数据,通过 reducer 传入

2.2.2 action

2.2.3 reducer

将传入的state和action结合起来生成一个新的state

2.3 Redux的三大原则

2.3.1 单一数据源

2.3.2 State是只读的

2.3.3 使用纯函数来执行修改

2.4 Redux 工作流程

建议看完Redux基本使用后再来看这幅图:

三、Redux基本使用

注意:以下 3 部分代码在 node 环境下

补充:node中对ES6模块化的支持

node v13.2.0开始,对ES6模块化提供了支持:

node v13.2.0之前,需要进行如下操作:

node v13.2.0之后,只需要进行如下操作:

3.1 创建Store的过程

定义reducer:必须是一个纯函数,不要直接修改state

createStore 传入 reducer

const { createStore } = require('redux')

// 初始化的数据
const initialState = {
  name: '李雷',
  counter: 100
}

// 定义reducer函数:纯函数
// 两个参数:
// 参数一:store中目前保存的state
// 参数二:本次需要更新的action(dispatch传入的action)
// 返回值:返回值会作为store之后存储的state
function reducer(state = initialState, action) {

  switch (action.type) {
    case 'change_name':
      return { ...state, name: action.name }

    case 'add_numer':
      return { ...state, counter: state.counter + action.num }

    default:
      return state
  }

}
// 创建store
const store = createStore(reducer)

module.exports = store

3.2 dispatch派发action

const store = require('./store')

console.log(store.getState()) // { name: '李雷', counter: 100 }

// 修改store中的数据:必须action
const nameAction = { type: 'change_name', name: '韩梅梅' }
store.dispatch(nameAction)
console.log(store.getState()) // { name: '韩梅梅', counter: 100 }

const nameAction2 = { type: 'change_name', name: '夏洛' }
store.dispatch(nameAction2)
console.log(store.getState()) // { name: '夏洛', counter: 100 }

// 修改counter
const counterAction = { type: 'add_numer', num: 10 }
store.dispatch(counterAction)
console.log(store.getState()) // { name: '夏洛', counter: 110 }

3.3 subscribe定位state

const store = require('./store')

const unSubscribe = store.subscribe(() => {
  console.log('订阅数据的变化:', store.getState())
})

// 修改store中的数据:必须action
store.dispatch({ type: 'change_name', name: '韩梅梅' })
store.dispatch({ type: 'change_name', name: '夏洛' })

// 取消订阅
unSubscribe()

// 修改counter
store.dispatch({ type: 'add_numer', num: 10 })

3.4 代码优化

示例:

actionCreators.js

const { ADD_NUMBER, CHANGE_NAME } = require("./constants")

const changeNameAction = (name) => ({
  type: CHANGE_NAME,
  name
})
const addNumberAction = (num) => ({
  type: ADD_NUMBER,
  num
})
module.exports = {
  changeNameAction,
  addNumberAction
}
const ADD_NUMBER = "add_number"
const CHANGE_NAME = "change_name"

module.exports = {
  ADD_NUMBER,
  CHANGE_NAME
}
const { CHANGE_NAME, ADD_NUMBER } = require('./constants')

// 初始化的数据
const initialState = {
  name: '李雷',
  counter: 100
}

function reducer(state = initialState, action) {
  switch (action.type) {
    case CHANGE_NAME:
      return { ...state, name: action.name }

    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num }

    default:
      return state
  }
}
module.exports = reducer
const { createStore } = require('redux')
const reducer = require('./reducer')

// 创建store
const store = createStore(reducer)

module.exports = store
const store = require('./store')

const { changeNameAction, addNumberAction } = require('./store/actionCreators')

store.dispatch(changeNameAction('独孤月'))
store.dispatch(addNumberAction(100))
console.log(store.getState()) // { name: '独孤月', counter: 200 }

四、Redux 在 React中使用

4.1 先来一个案例

有两个组件,组件上展示同一个counter,并且两者能够对counter进行操作

actionCreators.js

import * as actionTypes from './constants'

export const addNumberAction = (num) => ({
  type: actionTypes.ADD_NUMBER,
  num
})

export const subNumberAction = (num) => ({
  type: actionTypes.SUB_NUMBER,
  num
})

constants.js

export const ADD_NUMBER = "add_number"
export const SUB_NUMBER = "sub_number"

reducer.js

import * as actionTypes from './constants'

const initialState = {
  counter: 100
}

function reducer(state = initialState, action) {
  switch (action.type) {
    case actionTypes.ADD_NUMBER:
      return { ...state, counter: state.counter + action.num }

    case actionTypes.SUB_NUMBER:
      return { ...state, counter: state.counter - action.num }

    default:
      return state
  }
}
export default reducer

index.js

import { createStore } from "redux"
import reducer from "./reducer"

const store = createStore(reducer)

export default store

组件中使用:

import React, { PureComponent } from 'react'

// 引入store
import store from '../store'
import { addNumberAction } from '../store/actionCreators'

export default class Home extends PureComponent {

  constructor() {
    super()

    this.state = {
      counter: store.getState().counter
    }
  }

  componentDidMount() {
    store.subscribe(() => {
      const state = store.getState()
      this.setState({ counter: state.counter })
    })
  }

  addNumber(num) {
    store.dispatch(addNumberAction(num))
  }

  render() {
    const { counter } = this.state
    return (
      <div>
        <h2>Home Counter: {counter}</h2>
        <div>
          <button onClick={e => this.addNumber(1)}>+1</button>
          <button onClick={e => this.addNumber(5)}>+5</button>
          <button onClick={e => this.addNumber(8)}>+8</button>
        </div>
      </div>
    )
  }
}
import React, { PureComponent } from 'react'

// 引入store
import store from '../store'
import { subNumberAction } from '../store/actionCreators'

export default class Profile extends PureComponent {
  constructor() {
    super()

    this.state = {
      counter: store.getState().counter
    }
  }
  componentDidMount() {
    store.subscribe(() => {
      const state = store.getState()
      this.setState({ counter: state.counter })
    })
  }
  subNumber(num) {
    store.dispatch(subNumberAction(num))
  }
  render() {
    const { counter } = this.state
    return (
      <div>
        <h2>Profile Counter: {counter}</h2>
        <div>
          <button onClick={e => this.subNumber(1)}>-1</button>
          <button onClick={e => this.subNumber(5)}>-5</button>
          <button onClick={e => this.subNumber(8)}>-8</button>
        </div>
      </div>
    )
  }
}

4.2 react-redux使用

安装:npm install react-redux

在使用时在入口文件中导入 Provider,传入 store

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

import { Provider } from 'react-redux';
import store from './store'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

在 About 组件中使用:

import React, { PureComponent } from 'react'

import { connect } from 'react-redux'
import { addNumberAction, subNumberAction } from '../store/actionCreators'

export class About extends PureComponent {

  clacNumber(num, isAdd) {
    if(isAdd) {
      this.props.addNumber(num)
    } else {
      this.props.subNumber(num)
    }
  }
  render() {
    const { counter } = this.props
    return (
      <div>
        <h2>About Counter: {counter}</h2>
        <button onClick={e => this.clacNumber(6, true)}>+6</button>
        <button onClick={e => this.clacNumber(9, true)}>+9</button>
        <button onClick={e => this.clacNumber(6, false)}>-6</button>
        <button onClick={e => this.clacNumber(9, false)}>-9</button>
      </div>
    )
  }
}
// connect() 返回值是一个高阶组件
// function mapStateToProps(state) {
//   return {
//     counter: state.counter
//   }
// }

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

const mapDispatchToProps = (dispatch) => ({
  addNumber: num => dispatch(addNumberAction(num)),
  subNumber: num => dispatch(subNumberAction(num))
})

export default connect(mapStateToProps, mapDispatchToProps)(About)

connect():

4.3 组件中的异步操作

4.3.1 类组件生命周期中请求数据

通过发起action将请求的数据保存到store中

action方法:

export const changeBannersAction = (banners) => ({
  type: actionTypes.CHANGE_BANNERS,
  banners
})

export const changeRecommendsAction = (recommends) => ({
  type: actionTypes.CHANGE_RECOMMENDS,
  recommends
})
import * as actionTypes from './constants'

const initialState = {
  counter: 100,

  banners: [],
  recommends: []
}

function reducer(state = initialState, action) {
  switch (action.type) {
    case actionTypes.CHANGE_BANNERS:
      return { ...state, banners: action.banners }

    case actionTypes.CHANGE_RECOMMENDS:
      return { ...state, recommends: action.recommends }

    default:
      return state
  }
}

export default reducer
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import axios from 'axios'
import { changeBannersAction, changeRecommendsAction } from '../store/actionCreators'

export class Category extends PureComponent {

  componentDidMount() {
    // 发送请求
    axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
      const banners = res.data.data.banner.list
      const recommends = res.data.data.recommend.list
      this.props.changeBanners(banners)
      this.props.changeRecommends(recommends)
    })
  }

  render() {
    return (
      <div>
        <h2>Category Page</h2>
      </div>
    )
  }
}

const mapDispatchToProps = (dispacth) => ({
  changeBanners: banners => dispacth(changeBannersAction(banners)),
  changeRecommends: recommends => dispacth(changeRecommendsAction(recommends))
})

export default connect(null, mapDispatchToProps)(Category)

4.3.2 使用中间件

上面的代码有一个缺陷:

如何将异步请求交给 Redux?

redux-thunk 做了什么呢

代码演示:

import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"

// 正常情况下 store.dispatch(object)
// 想要派发函数 store.dispatch(function)

// applyMiddleware 可以传入多个中间件,","隔开
const store = createStore(reducer, applyMiddleware(thunk))

export default store
import * as actionTypes from './constants'
import axios from 'axios'

export const changeBannersAction = (banners) => ({
  type: actionTypes.CHANGE_BANNERS,
  banners
})

export const changeRecommendsAction = (recommends) => ({
  type: actionTypes.CHANGE_RECOMMENDS,
  recommends
})

export const fetchHomeMultidataAction = () => {
  // 如果是一个普通的action,需要返回action对象
  // 问题: 对象中不能直接拿到从服务器请求的异步数据

  // redux 不允许返回一个函数,需要中间件
  return (dispatch, getState) => {
    // console.log(getState().counter) // 100
    // 进行异步操作: 网络请求
    axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
      const banners = res.data.data.banner.list
      const recommends = res.data.data.recommend.list
      // dispatch({type: actionTypes.CHANGE_BANNERS, banners})
      // dispatch({type: actionTypes.CHANGE_RECOMMENDS, recommends})
      dispatch(changeBannersAction(banners))
      dispatch(changeRecommendsAction(recommends))
    })
  }

}
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { fetchHomeMultidataAction } from '../store/actionCreators'

export class Category extends PureComponent {

  componentDidMount() {
    this.props.fetchHomeMultidata()
  }

  render() {
    return (
      <div>
        <h2>Category Page</h2>
      </div>
    )
  }
}

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

const mapDispatchToProps = (dispacth) => ({
  fetchHomeMultidata: () => dispacth(fetchHomeMultidataAction())
})

export default connect(mapStateToProps, mapDispatchToProps)(Category)

4.4 redux-devtools

redux可以方便的对状态进行跟踪和调试

安装该工具需要两步:

默认该工具是未开启的,开发环境开启需要进行配置,生产环境千万千万不要打开哦!!!

import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"

// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store
复制代码

4.5 模块拆分

正常情况下,我们的 store 中应该是有不同状态的数据,比如:购物车、用户信息等等, 如果将所有的状态都放到一个reducer中进行管理,随着项目的日趋庞大,必然会造成代码臃肿、难以维护。因此,我们可以对reducer进行拆分

以上面提到的案例为例,抽取一个 counter 的reducer和一个 home 的reducer,再将其合并起来

分不同的模块,每个模块都包含自己的核心:

>reducer:接收action对象,返回最新的state

在 index.js 中导入每一个模块的内容,通过combineReducers合并之后放入createStore

import { createStore, applyMiddleware, compose, combineReducers } from "redux"
import thunk from "redux-thunk"

import counterReducer from './counter'
import homeReducer from './home'
import userReducer from './user'

// 将reducer合并到一起
const reducer = combineReducers({
  counter: counterReducer,
  home: homeReducer,
  user: userReducer
})

// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

combineReducers 如何实现合并呢?

// combineReducers 原理
function reducer(state = {}, action) {

  // 返回一个对象,store中的state
  return {
    counter: counterReducer(state.counter, action),
    home: homeReducer(state.home, action),
    user: userReducer(state.user, action)
  }
}

加载全部内容

相关教程
猜你喜欢
用户评论