Redux详解(一)

1. Redux的作用

React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:
  • 无论是组件定义自己的state,还是组件之间的通信通过props进行传递;也包括通过Context进行数据之间的共享;
  • React主要负责帮助我们管理视图,state如何维护最终还是我们自己来决定;
Redux就是帮助react管理state的容器Redux是JavaScript的状态容器,提供了可预测的状态管理;
Redux是一个独立的npm库,并不只能适用于React,也可以与其他javaScript构建界面库使用,具有较高的灵活性,且小体积的特点;

2. Redux核心理念 - Store

对于多个页面共享的一个数据时,需要定义统一的规范来操作数据,才能使整个数据的变化可追踪;

3. Redux核心理念 - action

Redux要求我们通过action来更新数据:
  • 所有数据的变化,必须通过派发(dispatch)action来更新;
  • action是一个普通的JavaScript对象,用来描述这次更新的type和content;
  • 强制使用action的好处是可以清晰的知道数据到底发生了什么样的变化,所有的数据变化都是可跟追、可预测的;
  • actions可以派发的是对象或者函数

4. Redux核心理念 - reducer

通过actions派发修改state状态的桥梁,就是reducer

  • reducer是一个纯函数;
  • reducer做的事情就是将传入的state和action结合起来生成一个新的state;
import * as actionsTypes from './constant'

const initialState = {
  counter: 10
}

function reducer(state = initialState, actions) {
  switch(actions.type) {
    case actionsTypes.ADD_NUMBER: 
      return {...state, counter: state.counter + actions.num}
    case actionsTypes.SUB_NUMBER: 
      return {...state, counter: state.counter - actions.num}
    default:
      return state
  }
}

export default reducer

5. Redux三大原则

单一数据源:

  • 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中;
  • Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;
  • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;

State是只读的:

  • 唯一修改State的方法一定是触发action不要试图在其他地方通过任何的方式来修改State;
  • 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;
  • 可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;
使用纯函数来执行修改:
  • 通过reducer将 旧state和 actions联系在一起,并且返回一个新的State;
  • 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers分别操作不同state tree的一部分;
  • 所有的reducer都应该是纯函数,不能产生任何的副作用;

6. Redux使用过程

1. 建一个对象,作为我们要保存的状态;

2. 创建Store来存储这个state;

  • 创建store时必须创建reducer;
  • 可以通过 store.getState 来获取当前的state;
3. 过action来修改state;
  • 通过dispatch来派发action;
  • 通常action中都会有type属性,也可以携带其他的数据;
4. 改reducer中的处理代码;
  • reducer是一个纯函数,不需要直接修改state;

5. Redux结构划分;

将所有的逻辑代码写到一起,那么当redux变得复杂时代码就难以维护。
分为以下几个文件模块

constant.js 定义type的常量

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

createActions.js dispatch派发的actions, 返回一个对象

import * as actionsType from './constant'

export const addNumberActions = (num) => ({
  type: actionsType.ADD_NUMBER,
  num
})

export const subNumberActions = (num) => ({
  type: actionsType.SUB_NUMBER,
  num
})

reducer.js 处理派发的actions,返回新的state 实现state状态更新

import * as actionsTypes from './constant'

const initialState = {
  counter: 10
}

function reducer(state = initialState, actions) {
  switch(actions.type) {
    case actionsTypes.ADD_NUMBER: 
      return {...state, counter: state.counter + actions.num}
    case actionsTypes.SUB_NUMBER: 
      return {...state, counter: state.counter - actions.num}
    default:
      return state
  }
}

export default reducer

index.js 导出reducer 

import reducer from "./reducer";

export default reducer
export * from "./createActions"

创建store

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

import counterReducer from './features/counter'
import homeReducer from './features/home'
import userInfoReducer from './features/userInfo'

const reducer = combineReducers({
  counter: counterReducer,
  home: homeReducer,
  userInfo: userInfoReducer
})

// 打开 redux-devtools  {trace: true} 追踪代码执行
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;

const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

7. Redux使用流程

注意:此图出自coderwhy老师的react课程讲解,有兴趣的小伙伴可以腾讯课堂搜索why老师课程学习

Redux官方文档对于Redux更新state的流程图表述:

8. Redux融入react使用

以counter数加减实现来说
核心代码主要是两个:
  • 在 componentDidMount 中定义数据的变化,当数据发生变化时重新设置 counter;
  • 在发生点击事件时,调用store的dispatch来派发对应的action;
8.1 react-redux使用

前文开头强调redux和react没有直接的关系,在React, Angular, Ember, jQuery, or vanilla JavaScript也完全可以使用Redux。

React官方为了在项目中更高效的使用redux,redux官方提供了react-redux库

npm install react-redux

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


const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(
  <Provider store={store}>
    <App/>
  </Provider>
  )

组件中:

import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumberActions, subNumberActions } from '../../store/features/counter'

export class About extends PureComponent {

  calcNumber(num, isAdd) {
    if(isAdd) {
      this.props.addNumber(num)
    } else {
      this.props.subNumber(num)
    }
  }

  render() {
    const { counter, banners, recommends } = this.props

    return (
      <div>
        <h2>About: counter: {counter}</h2>
        <div>
          <button onClick={e =>this.calcNumber(5, true)}>+5</button>
          <button onClick={e =>this.calcNumber(5, false)}>-5</button>
        </div>
        <div>
          <h2>banners数据:</h2>
          <ul>
            {
              banners.map((item, index) => {
                return <li key={index}>{item.title}</li>
              })
            }
          </ul>
        </div>
        <div>
          <h2>recommends数据:</h2>
          <ul>
            {
              recommends.map((item, index) => {
                return <li key={index}>{item.title}</li>
              })
            }
          </ul>
        </div>
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  counter: state.counter.counter,
  banners: state.home.banners,
  recommends: state.home.recommends
})

const mapDispatchToProps = (dispatch) => ({
  addNumber(num) {
    dispatch(addNumberActions(num))
  },
  subNumber(num) {
    dispatch(subNumberActions(num))
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(About)
8.2 组件中的异步操作

对于网络请求中的数据,需要交给redux来处理,需要通过中间件(middleware)来实现

理解中间件:

  • 中间件的目的是在dispatch的action和最终达到的reducer之间,扩展一些自己的代码;
  • 比如日志记录、调用异步接口、添加代码调试功能等等;
对于发送异步的网络请求,通过官网推荐的、包括演示的网络请求的中间件是使用 redux-thunk
  • 默认情况下的dispatch(action),action需要是一个JavaScript的对象;
  • redux-thunk可以让dispatch(action函数),action可以是一个函数;
  • 该函数会被调用,并且会传给这个函数一个dispatch函数和getState函数
    • dispatch函数用于我们之后再次派发action;
    • getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态;

npm install redux-thunk

const store = createStore(reducer, applyMiddleware(thunk))
export const fetchHomeMultidataActions = () => {
  return (dispatch, getData) => {
    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(changeBannersActions(banners))
      dispatch(changeRecommendsActions(recommends))
    })
  }
}
8.3 store的模块化分
目前我们合并的方式是通过每次调用reducer函数自己来返回一个新的对象,事实上,redux给我们提供了一个combineReducers函数可以方便的让我们对多个reducer进行合并:
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'

const reducer = combineReducers({
  counter: counterReducer,
  home: homeReducer,
  userInfo: userInfoReducer
})
combineReducers实现原理
  • 将我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);
  • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state;
  • 新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;​​​​​​​
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值