《一》Redux

基于 Redux4。

Redux 不是 Facebook 开发的,它是一个专门用于做状态管理的 JS 库,它的作用是集中式管理应用中多个组件共享的状态。Redux 和 React 并没有直接的关系,可以将 Redux 与 React、Vue 或任何其他类似的界面库一起使用。

React 是在视图层解决了 DOM 的渲染问题(开发者编写 JSX,React 生成虚拟 DOM,最终渲染成真实 DOM),但是状态 State 依然是留给开发者自己来管理。

Redux 的使用总结:

  1. 存储数据:通过 createStore() 生成一个 Store,用于存储数据。虽然 Store 是存储数据的地方,但 Store 中的数据都来源于 Reducer 函数的返回,因此 createStore() 要接受一个 Reducer 作为参数。
  2. 获取数据:通过 store.getState() 即可获取到更新后的 State。
  3. 定义更新:定义一个 Action 用于描述要更新的数据类型和内容。
  4. 通知更新:通过 store.dispatch(action) 派发一个 Action 给 Reducer,触发 Reducer 的自动执行。
  5. 返回更新:Reducer 函数通过计算返回一个新的 State 保存于 Store 中。
  6. 监听更新:通过 store.subscribe() 即可监听 Store 中数据的变化。

在这里插入图片描述

安装 Redux:

执行 npm install redux 安装 Redux。

Redux 的核心理念:

Redux 有三个核心理念:Store、Action、Redux。

Store:

Store:存储数据的地方。最好整个应用只有一个 Store。

createStore()

createStore() :用来生成 Store。接收 Reducer 作为其参数。

虽然 Store 是存储数据的地方,但 Store 中的数据都来源于 Reducer 函数返回的 State。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 作为参数传入 createStore() 方法。

import { createStore } from 'redux'
const store = createStore(reducer)
store.getState()

某个时间点的数据集合,就叫做 State,可以通过 store.getState() 获取到当前时刻的 State。

const state = store.getState()

示例:

// store.js
// 引入 createStore,用于创建 Store
const {createStore} = require("redux")

// 创建 Reducer,接收两个参数,分别是当前的 State 和本次被派发的 Action,根据被派发的 Action 返回新的 State 存储在 Store 中
function reducer(state, action) {
  console.log(state, action) // 初始加载  Store 会自动调用一次 Reducer 进行初始化状态,此时 state 是 undefined,action 对象中的 type 为 @@redux/INITxxx

  return {
    name: 'Lee'
  }
}

// 创建 Store,传入 Reducer 作为其参数
const store = createStore(reducer)

// 导出 Store
module.exports = store
// index.js
// 引入 Store,初始加载 Store 会自动调用一次 Reducer 进行初始化状态
const store = require('./store')

// 获取 Store 中的数据
console.log(store.getState()) // {name: 'Lee'}

请添加图片描述

Action:

Action:Action 就是一个普通的 JS 对象,用于描述要更新的数据类型和内容,其中 type 属性是必须的,表示 Action 的名称,其他属性可以自由设置。

const action = {
  type: 'ADD_TODO',
  payload: 'Learn Redux'
}
store.dispatch()

store.dispatch():所有数据的变化,必须通过派发(dispatch) Action 来更新。接受一个 Action 对象作为参数,将其发送出去。

store.dispatch() 方法会派发一个 Action 给 Reducer,作为 Reducer 的第二个参数,会触发 Reducer 的自动执行。

store.dispatch(action)

示例:

// store.js
// 引入 createStore,用于创建 Store
const {createStore} = require("redux")

// 创建 Reducer,接收两个参数,分别是当前的 State 和本次被派发的 Action,根据被派发的 Action 返回新的 State 存储在 Store 中
function reducer(state, action) {
  console.log(state, action) // 初始加载  Store 会自动调用一次 Reducer 进行初始化状态;派发 Action,Reducer 也会被自动执行
  switch (action.type) {
    // 匹配到 Action,则根据 Action 返回最新的 State
    case 'change_name':
      return {...state, name: action.name}
    // 否则返回默认值
    default:
      return {name: 'Lee'}
  }
}

// 创建 Store,传入 Reducer 作为其参数
const store = createStore(reducer)

// 导出 Store
module.exports = store
// index.js
// 引入 Store,初始加载 Store 会自动调用一次 Reducer 进行初始化状态
const store = require('./store')

// 获取 Store 中的数据
console.log(store.getState())

// 修改 Store 中的数据
// 1. 定义 Action,用于描述要修改的数据类型和内容
const action = {
  type: 'change_name',
  name: 'Mary',
}
// 2. 派发 Action,Reducer 会被自动执行,Action 会被传入 Reducer 中作为第二个参数
store.dispatch(action)

// 再次获取 Store 中的数据
console.log(store.getState())

请添加图片描述

Action Creator

Action 如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator

// 定义 Action Creator,只需传入想要修改的数据即可
const changeNameAction = name => ({
  type: 'chanhe_name',
  name,
})

// 使用 Action Creator 生成 Action
const action = changeNameAction('Mary')

Reducer:

Reducer:Store 接收到 Action 以后,必须给出一个新的 State,这种 State 的计算过程就叫做 Reducer。Reducer 是一个纯函数,它接受当前 State 和 Action 作为参数,返回一个新的 State。

Reducer 是一个纯函数,因此不能直接修改传入的 State,而需生成一个新的 State 返回。

初次加载 Store 会自动调用一次 Reducer 进行初始化状态,此时 state 是 undefined,action 对象中的 type 为 @@redux/INITxxx
手动调用 store.dispatch() 也会触发 Reducer 的自动执行。

一旦 Reducer 返回新的 State,Store 就会更新存储的数据。

const reducer = function (state, action) {
  ...
  return newState
}
store.subscribe()

store.subscribe():用于设置监听函数。接收一个回调函数作为监听函数,一旦 State 发生变化,就会自动执行监听函数。返回值也是一个函数,调用返回的函数就可以解除监听。

const unsubscribe = store.subscribe(() =>
  console.log(store.getState())
)

unsubscribe()

示例:

// store.js
// 引入 createStore,用于创建 Store
const {createStore} = require("redux")

// 创建 Reducer,接收两个参数,分别是当前的 State 和本次被派发的 Action,根据被派发的 Action 返回新的 State 存储在 Store 中
function reducer(state, action) {
  switch (action.type) {
    // 匹配到 Action,则根据 Action 返回最新的 State
    case 'change_name':
      return {...state, name: action.name}
    case 'change_age':
      return {...state, age: state.age + action.age}
    // 否则返回默认值
    default:
      return {name: 'Lee', age: 18}
  }
}

// 创建 Store,传入 Reducer 作为其参数
const store = createStore(reducer)

// 导出 Store
module.exports = store
// index.js
// 引入 Store,初始加载 Store 会自动调用一次 Reducer 进行初始化状态
const store = require('./store')

// 监听 Store 中数据的变化。只要 Store 中的数据发生变化,就会自动回调监听函数
store.subscribe(() => {
  console.log(store.getState())
})

// 修改 Store 中的数据
store.dispatch({
  type: 'change_name',
  name: 'Mary',
})

// 再次修改 Store 中的数据
store.dispatch({
  type: 'change_age',
  age: 2,
})

请添加图片描述

combineReducers()

当项目应用变得复杂,Store 必定会变成一个非常庞大的对象,而在整个应用中,有些 State 之间是互不关联的,因此可以拆分 Reducer,不同的 Reducer 处理不同的 Action 返回不同的 State。

// 不拆分的 Reducer
function countreducer(state, action) {
  // 匹配到 Action,则根据 Action 返回最新的 State
  switch(action.type) {
  	case 'change_name':
      return {...state, name: action.name}
    case 'change_age':
      return {...state, age: state.age + action.age}
    case 'increase':
      return {...state, count: state.count + action.count}
    case 'decrease':
      return {...state, count: state.count - action.count}
    // 否则返回默认值
    default:
      return {name: 'Lee', age: 18, count: 10}
  }
}
// 拆分后的 Reducer
function userReducer(state, action) {
  switch (action.type) {
    // 匹配到 Action,则根据 Action 返回最新的 State
    case 'change_name':
      return {...state, name: action.name}
    case 'change_age':
      return {...state, age: state.age + action.age}
    // 否则返回默认值
    default:
      return {name: 'Lee', age: 18}
  }
}
function countReducer(state, action) {
  // 匹配到 Action,则根据 Action 返回最新的 State
  switch(action.type) {
    case 'increase':
      return {...state, count: state.count + action.count}
    case 'decrease':
      return {...state, count: state.count - action.count}
    // 否则返回默认值
    default:
      return {count: 10}
  }
}

combineReducers():合并 Reducer,把多个小的 Reducer 合并成一个大的 Reducer,每个小的 Reducer 只需负责自己管理的 State,大的 Reducers 就可以传入 createStore() 中。接收一个 key: value 形式的对象,key 值是小的 Reducer 命名,用来控制 State 中 key 的命名,value 值就是小的 Reducer。

combineReducers() 的实现原理:

const combineReducers = reducers => {
  // 返回一个合并后的 Reducer 函数
  return (state = {}, action) => {
    const newState = {...state}

    ...  // 此处省略新旧 State 的对比
    
    // 根据传入对象的 key 获取到对应的 State 
    const reducerKeys = Object.keys(reducers)
    for (let i = 0 ; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[i]
      newState[key] = reducer(newState[key], action)
    }

    return newState
  }
}

例如:

combineReducers({
	reducer1: reducerFunc1,
	reducer2: reducerFunc2,
})

那么,State 对象的结构是:

{
	reducer1: ...,
	reducer2: ...,
}
import {createStore, combineReducers} from 'redux'
// 合并拆分后的 Reducer
const reducer = combineReducers({
	user: userReducer,
	count: countReducer,
})
// 关联 Store 和合并后的 Reducer
const store = createStore(reducer)

// 访问 name 属性:
const name = store.getState().user.name

Redux 的三大原则:

  1. 单一数据源:整个应用程序的 State 被存储在一棵 object tree 中,并且这棵 object tree 只存储在一个 Store 中。单一数据源可以让整个应用程序的 State 变得方便维护、修改、追踪。

    Redux 并没有强制不能创建多个 Store,但是那么做不利于数据的维护。

  2. State 是只读的:唯一修改 State 的方法就是触发 Action,不要试图在其他地方通过任何的方式来修改 State。这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行。
  3. 使用纯函数来执行修改:通过 Reducer 将旧的 State 和 Action 联系在一起,返回一个新的 State。所有的 Reducer 都应该是纯函数,不能产生任何的副作用。

在 React 项目中使用原生 Redux:

在 React 项目中使用原生 Redux 会有些繁琐,不推荐。

以下示例会发现:increase.jsxdecrease.jsx 中存在很多重复代码,其实可以使用高阶组件,将这部分重复代码进行抽取后,返回一个增强后的新组件。这也就是 react-redux 的实现原理。
请添加图片描述

// store/index.js
import {createStore} from 'redux'
import reducer from './reducer'

// 创建 Store 并导出
export default createStore(reducer)
// store/reducer.js
const initialState = {
  count: 10,
}

// 创建 Reducer 函数,根据不同的 Action 返回不同的 State
export default (state = initialState, action) => {
  switch(action.type) {
    case 'increase':
      return {...state, count: state.count + action.count}
    case 'decrease':
      return {...state, count: state.count - action.count}
    default:
      return state
  }
}
// store/action.js
// 创建 Action 的 Action Creator
export const increaseAction = count => ({
  type: 'increase',
  count,
})

export const decreaseAction = count => ({
  type: 'decrease',
  count,
})
// coponents/increase.jsx
import React, { PureComponent } from 'react'
import store from '../store'
import {increaseAction} from '../store/action'

export default class Increase extends PureComponent {
  state = {
  	// 通过 store.getstate() 方法来获取 Store 中数据的初始值
    count: store.getState().count, 
  }

  componentDidMount() {
    // 通过 store.subscribe() 方法来监听 Store 中数据的变化
     this.unsubscribe = store.subscribe(() => {
      const {count} = store.getState()
      this.setState({count}) // 将组件的 setState() 方法放入监听函数中,就可以实现 Store 中数据变化后组件的自动渲染
    })
  }

  componentWillUnmount() {
    // 取消监听 Store 中数据的变化
    this.unsubscribe()
  }

  handleIncrease = () => {
    // 通过 store.dispatch() 方法派发 Action 来修改 Store 中的数据
    store.dispatch(increaseAction(1))
  }

  render() {
    return (
      <div>
        <div>Increase Count:{this.state.count}</div>
        <button onClick={this.handleIncrease}>+1</button>
      </div>
    )
  }
}
// components/decrease.jsx
import React, { PureComponent } from 'react'
import store from '../store'
import {decreaseAction} from '../store/action'

export default class Decrease extends PureComponent {
  state = {
  	// 通过 store.getstate() 方法来获取 Store 中数据的初始值
    count: store.getState().count,
  }

  componentDidMount() {
    // 通过 store.subscribe() 方法来监听 Store 中数据的变化
     this.unsubscribe = store.subscribe(() => {
      const {count} = store.getState()
      this.setState({count})  // 将组件的 setState() 方法放入监听函数中,就可以实现 Store 中数据变化后组件的自动渲染。
    })
  }

  componentWillUnmount() {
    // 取消监听 Store 中数据的变化
    this.unsubscribe()
  }

  handleDecrease = () => {
    // 通过 store.dispatch() 方法派发 Action 来修改 Store 中的数据
    store.dispatch(decreaseAction(1))
  }

  render() {
    return (
      <div>
        <div>Increase Count:{this.state.count}</div>
        <button onClick={this.handleDecrease}>-1</button>
      </div>
    )
  }
}
// App.jsx
import React, { PureComponent } from 'react'
import Increase from './components/increase'
import Decrease from './components/decrease'

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Increase />
        <Decrease />
      </div>
    )
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值