React

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > React > React Redux使用

一文详解React Redux使用方法

作者:Rsquo

这篇文章主要介绍了一文详解React Redux使用方法,文章围绕主题展开详细的内容戒杀,具有一定的参考价值,需要的小伙伴可以参考一下

一、理解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)
  }
}

到此这篇关于一文详解React Redux使用方法的文章就介绍到这了,更多相关React Redux使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文