image.png

使用场景

Redux 的适用场景:多交互、多数据源。
从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

    工作流程

    首先,用户发出 Action。
    1. store.dispatch(action);
    然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。
    1. let nextState = todoApp(previousState, action);
    State 一旦有变化,Store 就会调用监听函数。
    1. // 设置监听函数
    2. store.subscribe(listener);
    listener可以通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
    1. function listerner() {
    2. let newState = store.getState();
    3. component.setState(newState);
    4. }

    API介绍

    action:

    View的展示是根据State数据而来的,想要View变化,只能改变State(存在store里),想要修改State就必须发出一个action通知,store接收到通知后处理state,从而改变View。
    Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置,
    1. const action = {
    2. type: "ADD",
    3. num: 1,
    4. }

    reducer:

    Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
    Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。
    1. const reducer = (state = 10, action) => {
    2. switch (action.type) {
    3. case "ADD":
    4. return state + action.num
    5. case "SQUARE":
    6. return state * state
    7. default:
    8. return state
    9. }
    10. }

    store:

    Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
    store就是把action和reducer联系到一起的对象,store本质上是一个状态树,保存了所有对象的状态。任何UI组件都可以直接从store访问特定对象的状态,其具有dispatch,subscribe,getState方法(敲黑板划重点)。
    1. import { createStore } from 'redux';
    2. const store = createStore(reducer);
    createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。 ```jsx import { createStore } from ‘redux’;

const initalState = { name:’ade’ }

const store = createStore(reducer,initalState);

  1. <a name="UCcwn"></a>
  2. #### store.subscribe()
  3. Store 允许使用store.subscribe方法**设置监听函数**,一旦 State 发生变化,就自动执行这个函数。
  4. ```jsx
  5. import { createStore } from 'redux';
  6. const store = createStore(reducer);
  7. store.subscribe(listener);

store.dispatch()

store.dispatch()是 View 发出 Action 的唯一方法,这就需要在View中引入store然后调用dispatch派发Action,dispatch一调用就会调用reducer来改变state从而改变View。

  1. store.dispatch(action)

store.getState()

getState方法可以获取返回当前state的值,可以在任意位置打印state的值。

  1. console.log(store.getState())

State

Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。
当前时刻的 State,可以通过store.getState()拿到。

  1. import { createStore } from 'redux';
  2. const store = createStore(fn);
  3. const state = store.getState();

示例

  1. import React from "react"
  2. import { createStore } from "redux"
  3. const addOne = {
  4. type: "ADD",
  5. num: 1,
  6. }
  7. const addTwo = {
  8. type: "ADD",
  9. num: 2,
  10. }
  11. const square = {
  12. type: "SQUARE",
  13. }
  14. const reducer = (state = 10, action) => {
  15. switch (action.type) {
  16. case "ADD":
  17. return state + action.num
  18. case "SQUARE":
  19. return state * state
  20. default:
  21. return state
  22. }
  23. }
  24. const store = createStore(reducer)
  25. // 获取state
  26. console.log(store.getState())
  27. // 操作
  28. console.log(store.dispatch(addOne))
  29. console.log(store.getState())
  30. console.log(store.dispatch(addTwo))
  31. console.log(store.getState())
  32. console.log(store.dispatch(square))
  33. console.log(store.getState())
  34. function App() {
  35. return (
  36. <div className='App'>
  37. <h1>123</h1>
  38. </div>
  39. )
  40. }
  41. export default App

中间件与异步操作

applyMiddleware

  1. import { applyMiddleware, createStore } from 'redux';
  2. import createLogger from 'redux-logger';
  3. const logger = createLogger();
  4. const store = createStore(
  5. reducer,
  6. applyMiddleware(logger)
  7. );

createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。

  1. const store = createStore(
  2. reducer,
  3. initial_state,
  4. applyMiddleware(logger)
  5. );

store.dispatch方法正常情况下,参数只能是对象,不能是函数。
所以使用redux-thunk中间件,改造store.dispatch,使得后者可以接受函数作为参数。

  1. import { createStore, applyMiddleware } from 'redux';
  2. import thunk from 'redux-thunk';
  3. import reducer from './reducers';
  4. // Note: this API requires redux@>=3.1.0
  5. const store = createStore(
  6. reducer,
  7. applyMiddleware(thunk)
  8. );

异步action

  1. const getAction = () => {
  2. return (dispatch, getState) => {
  3. fetch("https://api.github.com/users/ruanyf")
  4. .then(res => res.json())
  5. .then(data => {
  6. console.log(data)
  7. dispatch({
  8. type: "GET",
  9. num: data.id,
  10. })
  11. })
  12. }
  13. }

React-Readux的用法

connect()

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

  1. import { connect } from 'react-redux'
  2. export default connect(mapStateToProps, mapDispatchToProps)(Container)

mapStateToProps()

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。

  1. const mapStateToProps = state => {
  2. return {
  3. num: state,
  4. }
  5. }

它接受state作为参数,返回一个对象。
mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。

  1. const mapStateToProps = (state, ownProps) => {
  2. return {
  3. isActive: ownProps.filter === state.visibilityFilter
  4. }
  5. }

mapDispatchToProps

mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。
也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
同样它接受2个参数 ,第一个是dispatch,第二个是ownProps

  1. const mapDispatchToProps = dispatch => {
  2. return {
  3. add: value => dispatch(addAction(value)),
  4. square: () => dispatch(squareAction()),
  5. get: () => dispatch(getAction()),
  6. }
  7. }

mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。

组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
React-Redux 提供Provider组件,可以让容器组件拿到state。

  1. import React, { useState } from "react"
  2. import Container from "./components/container"
  3. import { Provider } from "react-redux"
  4. import store from "./store/store"
  5. function App() {
  6. return (
  7. <div className='App'>
  8. <Provider store={store}>
  9. <Container />
  10. </Provider>
  11. </div>
  12. )
  13. }
  14. export default App

示例

APP.js 使用Provider 注入store到app.js供全局使用

  1. import React, { useState } from "react"
  2. import Container from "./components/container"
  3. import { Provider } from "react-redux"
  4. import store from "./store/store"
  5. function App() {
  6. return (
  7. <div className='App'>
  8. <Provider store={store}>
  9. <Container />
  10. </Provider>
  11. </div>
  12. )
  13. }
  14. export default App

reducer.js 定义这个组件的 Reducer。

  1. const math = (state = 10, action) => {
  2. switch (action.type) {
  3. case "ADD":
  4. return state + action.num
  5. case "SQUARE":
  6. return state * 2
  7. case "GET":
  8. return action.num
  9. default:
  10. return state
  11. }
  12. }
  13. export default math

生成store对象,并使用Provider在根组件外面包一层。

  1. import math from "../reducer/math"
  2. import { createStore, applyMiddleware } from "redux"
  3. import thunk from "redux-thunk"
  4. const store = createStore(math, applyMiddleware(thunk))
  5. export default store
  1. import React, { useState } from "react"
  2. import { addAction, squareAction, getAction } from "../actions/actions"
  3. import { connect } from "react-redux"
  4. const Container = props => {
  5. console.log(props)
  6. const { num, add, square, get } = props
  7. return (
  8. <div>
  9. <button onClick={() => {add(1)}}>+1</button>
  10. <button onClick={() => {add(2)}}>+1</button>
  11. <button onClick={() => {square()}}>+1</button>
  12. <button onClick={() => {get()}}>+1</button>
  13. <h1>{num}</h1>
  14. </div>
  15. )
  16. }
  17. const mapStateToProps = state => {
  18. return {
  19. num: state,
  20. }
  21. }
  22. const mapDispatchToProps = dispatch => {
  23. return {
  24. add: value => dispatch(addAction(value)),
  25. square: () => dispatch(squareAction()),
  26. get: () => dispatch(getAction()),
  27. }
  28. }
  29. export default connect(mapStateToProps, mapDispatchToProps)(Container)

action.js

  1. import { ADD, SQUARE } from "../types/types"
  2. const addAction = num => {
  3. return {
  4. type: ADD,
  5. num,
  6. }
  7. }
  8. const squareAction = () => {
  9. return {
  10. type: SQUARE,
  11. }
  12. }
  13. const getAction = () => {
  14. return (dispatch, getState) => {
  15. fetch("https://api.github.com/users/ruanyf")
  16. .then(res => res.json())
  17. .then(data => {
  18. console.log(data)
  19. dispatch({
  20. type: "GET",
  21. num: data.id,
  22. })
  23. })
  24. }
  25. }
  26. export { addAction, squareAction, getAction }

上面的代码是不是看着很复杂,没错很复杂,那我们用一个新的插件Redux Toolkit。

Redux Toolkit

没错上面 reducer action state的代码可以直接简化为一个文件。

  1. import { createSlice } from "@reduxjs/toolkit"
  2. const initialSate = {
  3. num: 0,
  4. }
  5. const mathCount = createSlice({
  6. name: "math",
  7. initialState: initialSate,
  8. reducers: {
  9. addNum: (state, action) => {
  10. state.num += action.payload
  11. },
  12. },
  13. })
  14. export const { addNum } = mathCount.actions
  15. export default mathCount.reducer

生成store

  1. import { configureStore } from "@reduxjs/toolkit"
  2. import mathCount from "../features/mathCount"
  3. export default configureStore({
  4. reducer: {
  5. math: mathCount,
  6. },
  7. })

在app.js注入

  1. import React, { useState } from "react"
  2. import Container from "./components/container"
  3. import { Provider } from "react-redux"
  4. import store from "./store/store"
  5. function App() {
  6. return (
  7. <div className='App'>
  8. <Provider store={store}>
  9. <Container />
  10. </Provider>
  11. </div>
  12. )
  13. }
  14. export default App

组件使用

  1. import React, { useState } from "react"
  2. import { useSelector, useDispatch } from "react-redux"
  3. import { addNum } from "../features/mathCount"
  4. const Container = props => {
  5. const dispatch = useDispatch()
  6. const { num } = useSelector(state => {
  7. return state.math
  8. })
  9. return (
  10. <div>
  11. <button
  12. onClick={() => {
  13. dispatch(addNum(1))
  14. }}
  15. >
  16. +1
  17. </button>
  18. <button
  19. onClick={() => {
  20. dispatch(addNum(10))
  21. }}
  22. >
  23. +10
  24. </button>
  25. <h1>{num}</h1>
  26. </div>
  27. )
  28. }
  29. export default Container

官方文档
使用Redux Toolkit简化Redux
Redux 入门教程