Redux核心概念
- store:状态管理对象
- state:状态对象
- reducer:纯函数,接收当前state和action然后生成新的state
- action generator:动作生成器(这是我胡诌的概念哈)
- action:动作,用于指示store如何更新状态
Redux核心流程
- 创建store:设计好state和action,确保reducer可以正确工作,然后基于reducer生成store
- 订阅行为:通过store.subscribe来设定好当state改变时需要触发的行为
- 更新state:
- 使用action generator生成action
- 通过store.dispatch派发action给store
- store使用reducer生成新状态
- store使用新状态触发订阅行为
import { createStore } from 'redux'
const reducer = (state = {count: 0}, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
// 写作action,其实是action generator
const actions = {
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
const store = createStore(reducer);
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
React-Redux
首先介绍两个概念:
- UI组件:不关心状态管理,传入什么就渲染什么
- 容器组件:需要与redux store进行交互,即获取&更新其状态
我们要在react中使用redux进行状态管理,需要解决两个问题:
- react组件如何获取redux state
- react组件如何派发redux action
解决这两个问题,分两步走:
首先,用Provider分发store
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
const store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
这样在App的子组件中就都可以访问到store了,然后对于容器组件我们需要将其与store连接起来,使得其能够获取redux state,以及派发redux action
import { connect } from 'react-redux'
import * as React from 'react'
interface IProps {
foo: string;
setFoo: (val: string) => void;
}
const App: React.FC<IProps> = props => {
// 经过connect后props中被注入了若干属性(来自redux state)
const { foo, setFoo } = props;
return (
<>
<span>{foo}</span>
<button onClick={() => setFoo('new foo')}>change</button>
</>
);
};
// 要从redux state中映射出哪些props,在此定义
const mapStateToProps = state => {
return {
foo: state.foo,
};
};
// 要如何将dispatch方法映射到props中,在此定义
const mapDispatchToProps = dispatch => {
return {
setFoo(value) {
dispatch({type: 'set', value});
}
}
};
// 将组件连接到store(寻找层级最近的Provider所提供的store)
export default connect(mapStateToProps, mapDispatchToProps)(App);
Redux-Saga
在react应用中光是使用react-redux大部分情况下是不完善的,因为它派发的action是同步触发state更新的,那么当我们想要执行异步操作时(读写文件,网络请求等)就只能先设法拿到异步结果再派发action,这样可不太优雅呢。为了让我们能够直接派发异步的action,Redux-Saga来了。
使用示例
// ********* app.jsx
class UserComponent extends React.Component {
...
onSomeButtonClicked() {
const { userId, dispatch } = this.props
dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
}
...
}
// ********* saga.js
import { takeEvery, takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import Api from '...'
// workder Saga : 将在 USER_FETCH_REQUESTED action 被发起时调用
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
/*
在每个 `USER_FETCH_REQUESTED` action 被发起时调用 fetchUser
允许并发(译注:即同时处理多个相同的 action)
*/
function* mySaga() {
yield* takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
/*
也可以使用 takeLatest
不允许并发,发起一个 `USER_FETCH_REQUESTED` action 时,
如果在这之前已经有一个 `USER_FETCH_REQUESTED` action 在处理中,
那么处理中的 action 会被取消,只会执行当前的
*/
function* mySaga() {
yield* takeLatest("USER_FETCH_REQUESTED", fetchUser);
}
// ********** main.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware(mySaga)
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
以上示例看下来,其实会发现redux-saga模式其实就是在react-redux模式的基础上,在创建store时添加一个saga中间件,令其监听某些异步action,监听到就执行异步动作,执行完就携带结果去派发同步action,然后就是触发state更新然后更新应用