文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文详解React Redux使用方法

2024-04-02 19:55

关注

一、理解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使用内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯