964453a7-cc75-44eb-b79c-cb7a3e770639.jpeg

学习React是绕不开Redux,就好像学习Vue是绕不开Vuex一样。虽然现在有些工具已经完全可以代替Redux,但依然有些旧项目使用着Redux,是必须掌握的一个工具。

基本概念

Redux主要的有三个概念:

  • State:即Store,一般来说是一个纯JavaScript Object;
  • Action:描述发生的动作;
  • Reducer: 是一个函数,用于接收Action和State 并作为参数,通过计算得到 Store;

用法为:

  1. import { createStore } from "redux";
  2. //定义默认的Store
  3. const initialState = { value: 0 }
  4. //Reducer,处理Action返回新的State
  5. const counterReducer = (state = initialState, action) => {
  6. switch(action.type) {
  7. case "counter/incremented":
  8. return { value: state.value + 1}
  9. case "counter/decremented":
  10. return { value: state.value - 1}
  11. default:
  12. return state
  13. }
  14. }
  15. //创建Store,参数是Reducer
  16. const store = createStore(counterReducer)
  17. //Store 提供了 subscribe 用于监听数据变化
  18. store.subscibe(() => {
  19. console.log(store.getState())
  20. })
  21. //计数器加1 用Store 的 dispatch 方法分发一个 Action,由 Reducer 处理
  22. const incrementAction = { type: "counter/incremented" }
  23. store.dispatch(incrementAction)
  24. // 监听函数输出:{value: 1}
  25. // 计数器减 1 ,同上
  26. const decrementAction = { type: 'counter/decremented' };
  27. store.dispatch(decrementAction)
  28. // 监听函数输出:{value: 0}

如何在React使用Redux?

如何在React与Redux之间建起联系,就需要用官方提供的 react-redux这个工具库。

该工具是利用了 React 的Context 机制去存放Store的信息。而在项目里,通常 Context 会作为整个React 应用程序的根节点。如下代码:

  1. import React from 'react'
  2. import ReactDOM from 'react-dom'
  3. import { Provider } from 'react-redux'
  4. import store from './store'
  5. import App from './App'
  6. const rootElement = document.getElementById('root')
  7. ReactDOM.render(
  8. <Provider store={store}>
  9. <App />
  10. </Provider>,
  11. rootElement
  12. )

这样一来,Provider就可以作为整个项目的根结点,并将Store传给这个组件,下层的所有组件都可以使用Redux。

完成以上配置后,就可以使用 react-redux 工具提供的 useSelector 和 useDispatch 这两个Hooks。

依然是计数器为例子:

  1. import React from 'react';
  2. import { useSelector, useDispatch } from 'react-redux';
  3. const Counter = () => {
  4. // 从 state 中获取当前的计数值
  5. const count = useSelector(state => state.value)
  6. // 获取当前的 store 的 dispatch 方法
  7. const dispatch = useDispatch()
  8. return (
  9. <>
  10. <button onClick={ () => dispatch({type: "counter/incremented"}) }>+</button>
  11. <span>{count}</span>
  12. <button onClick={ () => dispatch({type: "counter/decremented"}) }>-</button>
  13. </>
  14. )
  15. }

如何处理Redux异步逻辑(进阶)

背景

有时候,需要Redux异步处理某些逻辑(例如请求某个数据),这样的处理方式叫做 异步Action

例如说,Store有一个代表请求的状态的数据 pending,如下表示状态:

  1. state.pending = true //开始请求数据时,用于UI显示加载中的状态;
  2. state.pending = false; state.data = res //请求成功时,取消UI显示加载中的状态,并把请求的数据放入data;
  3. state.pending = false; state.error = err //请求失败时,取消UI显示加载中的状态,并把请求失败的消息放入 error;

以上三种状态,就需要用到三个Action来完成,如下代码:

  1. const Datalist = () => {
  2. const dispatch = useDispatch();
  3. useEffect(() => {
  4. //发送请求
  5. dispatch({ type: 'FETCH_DATA_BEGIN' });
  6. fetch('想请求的url').then(res => {
  7. //请求成功时
  8. dispatch({type: 'FETCH_DATA_SUCCESS', data: res})
  9. }).catch(err => {
  10. dispach({ type: 'FETCH_DATA_FAILURE', error: err})
  11. })
  12. }, [])
  13. const data = useSelector(state => state.data)
  14. const pending = useSelector(state => state.pending)
  15. const error = useSelector(state => state.error)
  16. if(error) return 'Error';
  17. if(pending) return 'Loading...';
  18. return (
  19. <>{data}</>
  20. )
  21. }

虽然上面的代码没有任何语法问题,也能跑起来。但是,这样很明显不能重用代码,假如别的页面也是这样,又要重新写一次这样的代码,就会出现重复代码。所以,Redux提供了一个机制,可以巧妙的实现异步Action的概念。

Middleware概念

Middleware可以在Action 到 Reducer之间,自由的调用Middleware,也是就拦截器。该拦截器可以对对Action做一些操作,并把这个操作结果传给Reducer。这个操作结果可以是新的Action,也可以直接把Action传过去。

简而言之,Middleware可以在Reducer之前,有一个额外的机会处理Action。
image.png

可以实现这个概念的,就需要“react-thunk”这个中间件来实现。需要注意Redux中的Action不单单只是Object,可以是别的东西,当然也可以是函数。

  1. 当这个中间件发现这个Action是一个函数时,就不会立即的传递Action,而是执行这个函数。
  2. 执行这个函数的参数则是由dispatch传递,并把返回的结果传递给Reducer。

根据以上例子,具体用法如下:

  1. 创建Redux Store时,指定redux-thunk。 ```jsx import { createStore, applyMiddleware } from ‘redux’; import thunkMiddleware from ‘redux-thunk’ import rootReducer from ‘./reducer’

const componsedEnhancer = applyMiddleware(thunkMiddleware) const store = createStore(rootReducer, componsedEnhancer)

  1. 2. 这样,在 dispatch Action 时,就可以dispatch 个函数
  2. ```jsx
  3. function fetchData(){
  4. return () => {
  5. dispatch({ type: 'FETCH_DATA_BEGIN' });
  6. fetch('想请求的url').then(res => {
  7. //请求成功时
  8. dispatch({type: 'FETCH_DATA_SUCCESS', data: res})
  9. }).catch(err => {
  10. dispach({ type: 'FETCH_DATA_FAILURE', error: err})
  11. })
  12. }
  13. }
  14. function DataList() {
  15. const dispatch = useDispatch()
  16. dispatch(fetchData())
  17. }

通过这样的方式,就可以实现异步请求方法的重用。