学习React是绕不开Redux,就好像学习Vue是绕不开Vuex一样。虽然现在有些工具已经完全可以代替Redux,但依然有些旧项目使用着Redux,是必须掌握的一个工具。
基本概念
Redux主要的有三个概念:
- State:即Store,一般来说是一个纯JavaScript Object;
- Action:描述发生的动作;
- Reducer: 是一个函数,用于接收Action和State 并作为参数,通过计算得到 Store;
用法为:
import { createStore } from "redux";
//定义默认的Store
const initialState = { value: 0 }
//Reducer,处理Action返回新的State
const counterReducer = (state = initialState, action) => {
switch(action.type) {
case "counter/incremented":
return { value: state.value + 1}
case "counter/decremented":
return { value: state.value - 1}
default:
return state
}
}
//创建Store,参数是Reducer
const store = createStore(counterReducer)
//Store 提供了 subscribe 用于监听数据变化
store.subscibe(() => {
console.log(store.getState())
})
//计数器加1 用Store 的 dispatch 方法分发一个 Action,由 Reducer 处理
const incrementAction = { type: "counter/incremented" }
store.dispatch(incrementAction)
// 监听函数输出:{value: 1}
// 计数器减 1 ,同上
const decrementAction = { type: 'counter/decremented' };
store.dispatch(decrementAction)
// 监听函数输出:{value: 0}
如何在React使用Redux?
如何在React与Redux之间建起联系,就需要用官方提供的 react-redux这个工具库。
该工具是利用了 React 的Context 机制去存放Store的信息。而在项目里,通常 Context 会作为整个React 应用程序的根节点。如下代码:
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
这样一来,Provider就可以作为整个项目的根结点,并将Store传给这个组件,下层的所有组件都可以使用Redux。
完成以上配置后,就可以使用 react-redux 工具提供的 useSelector 和 useDispatch 这两个Hooks。
依然是计数器为例子:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
const Counter = () => {
// 从 state 中获取当前的计数值
const count = useSelector(state => state.value)
// 获取当前的 store 的 dispatch 方法
const dispatch = useDispatch()
return (
<>
<button onClick={ () => dispatch({type: "counter/incremented"}) }>+</button>
<span>{count}</span>
<button onClick={ () => dispatch({type: "counter/decremented"}) }>-</button>
</>
)
}
如何处理Redux异步逻辑(进阶)
背景
有时候,需要Redux异步处理某些逻辑(例如请求某个数据),这样的处理方式叫做 异步Action
例如说,Store有一个代表请求的状态的数据 pending,如下表示状态:
state.pending = true
//开始请求数据时,用于UI显示加载中的状态;state.pending = false; state.data = res
//请求成功时,取消UI显示加载中的状态,并把请求的数据放入data;state.pending = false; state.error = err
//请求失败时,取消UI显示加载中的状态,并把请求失败的消息放入 error;
以上三种状态,就需要用到三个Action来完成,如下代码:
const Datalist = () => {
const dispatch = useDispatch();
useEffect(() => {
//发送请求
dispatch({ type: 'FETCH_DATA_BEGIN' });
fetch('想请求的url').then(res => {
//请求成功时
dispatch({type: 'FETCH_DATA_SUCCESS', data: res})
}).catch(err => {
dispach({ type: 'FETCH_DATA_FAILURE', error: err})
})
}, [])
const data = useSelector(state => state.data)
const pending = useSelector(state => state.pending)
const error = useSelector(state => state.error)
if(error) return 'Error';
if(pending) return 'Loading...';
return (
<>{data}</>
)
}
虽然上面的代码没有任何语法问题,也能跑起来。但是,这样很明显不能重用代码,假如别的页面也是这样,又要重新写一次这样的代码,就会出现重复代码。所以,Redux提供了一个机制,可以巧妙的实现异步Action的概念。
Middleware概念
Middleware可以在Action 到 Reducer之间,自由的调用Middleware,也是就拦截器。该拦截器可以对对Action做一些操作,并把这个操作结果传给Reducer。这个操作结果可以是新的Action,也可以直接把Action传过去。
简而言之,Middleware可以在Reducer之前,有一个额外的机会处理Action。
可以实现这个概念的,就需要“react-thunk”这个中间件来实现。需要注意Redux中的Action不单单只是Object,可以是别的东西,当然也可以是函数。
- 当这个中间件发现这个Action是一个函数时,就不会立即的传递Action,而是执行这个函数。
- 执行这个函数的参数则是由dispatch传递,并把返回的结果传递给Reducer。
根据以上例子,具体用法如下:
- 创建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)
2. 这样,在 dispatch Action 时,就可以dispatch 一 个函数
```jsx
function fetchData(){
return () => {
dispatch({ type: 'FETCH_DATA_BEGIN' });
fetch('想请求的url').then(res => {
//请求成功时
dispatch({type: 'FETCH_DATA_SUCCESS', data: res})
}).catch(err => {
dispach({ type: 'FETCH_DATA_FAILURE', error: err})
})
}
}
function DataList() {
const dispatch = useDispatch()
dispatch(fetchData())
}
通过这样的方式,就可以实现异步请求方法的重用。