React06-认识Redux

56 阅读4分钟

Redux

react 中最常用状态管理库😄

纯函数

纯函数:①确定的输入,一定会产生确定的输出。②函数在执行过程中,不能产生副作用。

  • slice 是纯函数
  • splice 不是
  • redux 中的 reducer 要被要求是一个纯函数
  • React 要求函数组件和类组件,必须像纯函数一样,保护它们的 props 不被修改

Redux基本使用

核心理念: store action reducer

React 可以用 Redux 来管理 state。注意 Redux 是可以脱离 React 的,比如在 Node 环境下亦可以使用

  • action

    所有数据的变化,都必须通过派发(dispatch) action 来实现,action 是一个普通的 JS 对象,用来描述这次更新的 typecontent

    action 对象类似于这样:

    • const action = { type: "INC_COUNT", count: 0}
    • store.dispatch(action)
  • reducer

    reducer 的作用是将传入的 state(旧的) 和 action 结合在一起生成一个新的 statereturn 出去作为新的 state

首先我们有一些基本数据

const intialState = {
    
}

怎么让 redux 管理呢?

import {createStore} from 'redux'function reducer() {}
​
const store = createStore(reducer)//该函数需要传入一个 reducer

reducer 中可以返回我们定义的 state

import {createStore} from 'redux'const intialState = {
    
}
​
function reducer() {
    return intialState
}
​
const store = createStore(reducer)//该函数需要传入一个 reducerexport default store

怎么获取 redux 中的 state 呢?

import store from './store'
console.log(store.getState())

怎么修改 redux 中的 state 呢?

要是能像 Pinia 那样,直接修改😄,怎么说?

store.getState().name = "newName"

千万不要这样改!😏

要这样😉

const nameAction = {type:"change_name", name:"kobe"}
store.dispatch(nameAction)

reducer怎么接受呢?😆

function reducer(state, action) {
    return initialState
}

参数 action 即可!😄

优化体验

我们会注意到,当 store 创建时 reducer 是会被调用一次的,此时最好传入初始 state

因此,准确的写法应该是

function reducer(state = initialState, action) {
    if (action.type === "change_name"){
        return { ...state, name: action.name }        
    }
    return state;
}

我们没必要每次派发完都手动的 getState(),我们希望每次修改 state 后,能自动刷新页面,这就需要我们订阅store

注意:此处的订阅只有当数据发生改变后才会执行,第一次加载时不会

store.subscribe(() => {
   store.getState() //当数据变化时会自动调用该函数
})

取消订阅

const unSubscribe = store.subscribe(() => {
   store.getState() //当数据变化时会自动调用该函数
})
​
unSubscribe()//取消订阅

if 语句优化

function reducer(state = initialState, action){
    switch(action.type) {
        case:"change_name":
            return {...state, name: action.name}
        case : "add_num":
            return {...state, num: state.num + action.num}
        default:
            return state
    }
}

动态生成 action

const changeNameAction = (name) => {
    return {
        type: "change_name",
        name
    }
}
​
store.dispatch(changeNameAction("kobe"))
store.dispatch(changeNameAction("kobe"))
​
const addNumAction = (num) =>{
    return {
        type: "add_num",
        num
    }
}
​
store.dispatch(addNumAction("2"))

你这个文件想派发 action,我这个文件也想派发 action,我们都想要派发 action 🔥🔥🔥

我们一般会在 store 文件夹下建立一手 actionCreator.js,在此文件中定义所有的 action 函数,然后哪个文件需要,就在哪个文件中引入即可。

对于 action 的类型,我们也在当前文件夹下建立一手保存常量的 constants.js,这样我们之后用常量防止出错

const ADD_NUMBER = "add_number"
const CHANGE_NAME = "change_name"module.exports = {
  ADD_NUMBER,
  CHANGE_NAME
}
​
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
}

一般情况下,reducer 函数会越来越复杂,所以通常情况下我们也把它单独抽离到一个文件中 reducer.js

const { ADD_NUMBER, CHANGE_NAME } = require("./constants")
​
// 初始化的数据
const initialState = {
  name: "why",
  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

总结一下上面做的几点优化:

  1. 将派发的action生成过程放到一个actionCreators函数中
  1. 将定义的所有actionCreators的函数, 放到一个独立的文件中: actionCreators.js
  1. actionCreatorsreducer函数中使用字符串常量是一致的, 所以将常量抽取到一个独立constants的文件中*
  1. reducer和默认值(initialState)放到一个独立的reducer.js文件中, 而不是在index.js

综上,早期的 redux 使用方式我们已经介绍完毕,一般目录结构就是如此😆

store
├─ actionCreators.js
├─ constants.js
├─ index.js
└─ reducer.js

Redux的三大原则

  • 单一数据源

    • 整个应用程序的 state 被存储在一颗 object tree 中,并且这棵树只存储在一个 store
    • 不像 piniaRedux 中你应该只创建一个 store
  • State中的数据应该是只读的

    • 修改 state 的唯一方式是派发 action
    • 这样可以避免 race condition (竞态)的问题(两个进程同时修改)
  • 只能通过纯函数来修改

    • 通过 reducer 将旧的 stateactions 结合,返回一个新的 state
    • 随着 reducer 日益增大,我们可以将其拆分成多个小的 reducers
    • reducer 应该是一个纯函数