原文收錄在github 倉庫 (包含小demo) github地址:點這裏 react
通用的狀態管理輔助工具
,習慣上咱們能夠結合ReactJs
來使用,在組件化開發過程當中,組件的數據狀態
不得不集中化管理,這也是咱們使用Redux
的緣由之一,是一個數據的容器。習慣上咱們稱之爲js庫
ios
action
顧名思義動做
,行動
行爲
,一言以蔽之,它是把數據從應用傳到倉庫
的一個動做,也就是這個數據倉庫git
JS對象es6
格式github
{
type:ADD, // type 字段是約定,你們的約定,表示動做的名字,
index:1,
content:'這是一點內容'
}
複製代碼
type
字段表示執行的動做;字符串常量在實際的開發中咱們習慣上是action建立函數typescript
const addAction = (params)=>{
return {
type:ADD,
...params
}
}
複製代碼
如今咱們依舊不說store
這個概念,如今動做
有了,可是action
它只是描述了一下這個動做,但並不知道咋更新數據,提到數據,咱們假使redux
{
num:0
}
複製代碼
這個簡單的js對象
就是數據axios
ACTION是個普通的對象;REDUCER是個普通的函數api
說普通也不普通,js的函數而已app
function(a,b){
console.log(a+b)
}
複製代碼
可是沒那麼簡單
乾淨簡單,
// 默認的數據
const initData = {
num:123
}
// reducer
const counterReducer =(state=initData,action)=>{
// 啥也不幹,返回傳進來的state(此時默認的initData)
return state
}
複製代碼
怎麼可能啥也不幹呢
import { addAction } from "./actions";
// 默認的數據
const initData = {
num: 123
};
// reducer
const counterReducer = (state = initData, action) => {
// 判斷傳入的動做是什麼類型
switch (action.type) {
case addAction:
return Object.assign({}, state, {
...
});
default:
return state;
}
// 啥也不幹,返回傳進來的state(此時默認的initData)
// return state;
};
複製代碼
注意
state
getState()
複製代碼
dispatch(action)
複製代碼
也就是咱們說的派發一個動做
subscribe(listener)
複製代碼
在這個時候,有個問題,前邊說的這一切,那咱們該怎麼來建立這個倉庫呢
yarn add redux
複製代碼
這個庫裏就有方法,也就是咱們常說的redux
import { ADD_TYPE } from './actionTypes'
const addAction = (params)=>{
return {
type:ADD_TYPE,
...params
}
}
export {
addAction
}
複製代碼
import { addAction } from "./actions";
// 默認的數據
// reducer
const counterReducer = (state = {num:123}, action) => {
// 判斷傳入的動做是什麼類型
switch (action.type) {
case addAction:
return Object.assign({}, state, action);
default:
return state;
}
// 啥也不幹,返回傳進來的state(此時默認的initData)
// return state;
};
export {
counterReducer
}
複製代碼
import { createStore } from "redux";
import { counterReducer } from "./reducers";
複製代碼
const store = createStore(counterReducer);
export default store
複製代碼
const handleClick = ()=>{
console.log(`點擊了按鈕`)
const action = addAction({num:'456'})
store.dispatch(action)
}
複製代碼
useEffect(() => {
store.subscribe(()=>{
console.log('-----',store.getState())
})
}, [])
複製代碼
const render = ()=>{
ReactDom.render( <App/>, document.querySelector('#root') ) }
// 上來的時候先渲染一次
render()
// 訂閱變動,每當數據發生的變化的時候,就從新渲染
store.subscribe(render)
複製代碼
經過一個簡單的案例,咱們知道一個簡易的流程:
action
返回一個對象必須有type屬性reducer
響應action t經過return 把數據傳回storeredux
這個庫來建立一個store 傳遞寫好的reducer
$store.subscribe()
註冊監聽store.getState()
取值那在如上咱們使用的redux
這個庫看起來是沒有問題,可是
這一波流的操做在每一個組件都要走一遍,顯然是十分繁瑣和重複的,這就須要看誰能不能幫幫我,這就是react-redux
若是須要把redux
整合到react
中來使用就須要react-redux
react-redux
redux 官方出品
可以更好的結合react
來管理數據
state
store
做爲props
經過context
傳遞store
中的state
connect
增強const mapStateToProps = (state, ownProps) => {
console.log(state)
return state
// return {
// prop: state.prop
// }
}
複製代碼
const mapDispatchToProps = (dispatch, ownProps) => {
return {
sendAction: () => {
dispatch({
type: "ADD_TYPE",
});
},
};
};
複製代碼
安裝相關的依賴
構建store 和readucer
Provider組件實現
<>
<Provider store = {store}>
<List></List>
<Detail></Detail>
</Provider>
</>
複製代碼
import { createStore, combineReducers } from "redux";
// import { counterReducer } from "./reducers";
// import rootReducer from './reducers/index'
import { infoReducer } from "./reducers/infoReducer";
import { listReducer } from "./reducers/listReducer";
const reducer = combineReducers({
infoReducer,
listReducer,
});
// 構建store
const store = createStore(reducer);
export default store;
複製代碼
ComA A組件
import React, { Component } from "react";
import { connect } from "react-redux";
class ComA extends Component {
handleClick = () => {
this.props.getInfo();
};
render() {
return (
<div>
{/* <h3>{this.props.}</h3> */}
<button onClick={this.handleClick}>獲取信息</button>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
console.log(state.infoReducer);
// return {
// prop: state.prop,
// };
// return state
return {
...state.infoReducer,
};
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
getInfo: () => {
const actionCreator = {
type: "GET_INFO",
};
dispatch(actionCreator);
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ComA);
複製代碼
ComB
import React, { Component } from "react";
import { connect } from "react-redux";
class ComB extends Component {
handleClick = () => {
this.props.getList();
};
render() {
return (
<div>
<button onClick={this.handleClick}>獲取列表</button>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
console.log(state.listReducer)
// return state
return {
...state.listReducer
}
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
getList: () => {
const actionCreator = {
type: "GET_LIST",
};
dispatch(actionCreator);
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ComB);
複製代碼
infoReducer.js
const info = {
name: "yayxs",
};
const infoReducer = (state = {}, action) => {
switch (action.type) {
case "GET_INFO":
return {
...info,
};
default:
return state;
}
};
export {
infoReducer
}
複製代碼
listReducer
const listArr = [
{
id: 1,
con: "耳機",
},
];
const listReducer = (state = {}, action) => {
switch (action.type) {
case "GET_LIST":
return {
listArr: [...listArr],
};
default:
return state;
}
};
export {
listReducer
}
複製代碼
無論怎麼說,如上說起數據流操做只支持同步的操做,實現異步的話就須要中間件
異步操做
,讓反作用的執行更加簡單其中源碼是這樣的
export default function createSagaMiddleware<C extends object>(options?: SagaMiddlewareOptions<C>): SagaMiddleware<C> export interface SagaMiddlewareOptions<C extends object = {}> {
/** * Initial value of the saga's context. */
context?: C
/** * If a Saga Monitor is provided, the middleware will deliver monitoring * events to the monitor. */
sagaMonitor?: SagaMonitor
/** * If provided, the middleware will call it with uncaught errors from Sagas. * useful for sending uncaught exceptions to error tracking services. */
onError?(error: Error, errorInfo: ErrorInfo): void
/** * Allows you to intercept any effect, resolve it on your own and pass to the * next middleware. */
effectMiddlewares?: EffectMiddleware[]
}
複製代碼
import createSagaMiddleware from "redux-saga";
複製代碼
const store = createStore(sagaReducer, {}, applyMiddleware(sagaMiddleware));
複製代碼
sagaMiddleware.run(defSage);
複製代碼
takeEvery
takeLatest
throttle
SagaCom
handleClick = (type) => {
switch (type) {
case "takeEvery":
this.props.dispatch({
type: "takeEvery",
});
break;
case "takeLatest":
this.props.dispatch({
type: "takeLatest",
});
break;
case "throttle":
this.props.dispatch({
type: "throttle",
});
break;
default:
break;
}
};
複製代碼
import {
takeEvery,
takeLatest,
throttle,
select,
call,
} from "redux-saga/effects";
import axios from "axios";
export function* defSage() {
yield takeEvery("takeEvery", function* () {
const state = yield select((state) => state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
// 最後的一次,取消正在運行中
yield takeLatest("takeLatest", function* () {
const state = yield select((state) => state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
/** * 毫秒值 */
yield throttle(0, "throttle", function* () {
const state = yield select((state) => state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
}
複製代碼
詳細的api 用法能夠參考官方文檔
action
reducer
匹配對應的action 若是是一部的action 直接把數據返回takeEvery
來進行監聽takeEvery
takeLatest
throttle
在底層有什麼區別?感謝你看到這,不妨給個星星,感謝