前言
redux核心很簡單。
redux
redux核心
redux 很簡單,總結起來就是一句話:一個帶事件通知的狀態保存者。
看代碼,如裏下面代碼看明白了,直接跳至 插件
const {createStore} = require('redux');
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
const subscriber = () => {
console.log("狀態變化了", store.getState())
};
store.subscribe(subscriber)
store.dispatch({type: 'add', payload: 2})
store.dispatch({type: 'dec', payload: 1})
運行結果
狀態變化了 { value: 3 }
狀態變化了 { value: 2 }
代碼極其簡單,但redux 的所有核心(全部)內容也就在這裏了。只有一個createStore函數,用來創建一個 store 來保存狀態,傳入的參數是一到三個。第一個參數是叫reducer函數, 作用是產生狀態,每二個參數是初始狀態,如果有第三個參數,則是插件。只有第一個參數是必須的。
store對象
就三個方法
- getState() , 用來取狀態
- dispatch(action),告訴store 一個事件,只要是個對象就行,對象格式無要求,只要reducer認識就行。大多數按官網寫法,寫成 {type:‘someKey’,payload:‘someValue’} ,代表事件名稱,及參數
- subscriber(callback) , 當狀態變化後,通知callback , 這個通知極其簡陋,只告訴你變化了,但不告訴你哪裏變化了。
reducer 函數
redux 把生成狀態的函數叫reducer ,接受兩個參數,第一個當前狀態,第二個就是事件內容。 只要注意一點:無論如何要返回一個狀態,比如下面的例子,缺少了default 的處理,就會出錯,因爲少了default處理,在初始化的時候狀態變成undefined
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value + action.payload}
// default: 這個註釋掉就出錯
// return state
}
}
redux 工作過程
初始化
createStore 調用reducer ,傳入一個參數 :初始狀態,也就是createStore的第二個參數。調用後,得到初始狀態,保存下來。
運行時
三步曲:
用dispatch 發一個事件 => store調用reducer=>發出通知 subscribe
插件
插件就是攔截 store.dispatch 方法,寫法是固定的,下面以一個日誌插件爲例
function logger({dispatch, getState}) {
//此dispatch 方法是未被攔截的dispatch
return (next) => {
// next 是指下一個插件
return (action) => {
//需要填寫的內容在此
console.log('before log', action, getState())
next(action)
console.log('after log', getState)
}
}
}
const store = createStore(reducer,applyMiddleware(logger))
插件可以形成一個串行調用鏈。上面例子中的 applyMiddleware 方法 由redux提供,參數是可以多個插件,作用就是將多個插件串起來。
redux 其它內容
redux 官網其它內容都是在講編程規範與技巧
取消“魔法”字符串和對象
在上述例子中,寫事件時,都是直接用字符串,比如"add",“dec”,如果多個地方用的話,會容易出錯。編程中將這類容易出錯的字符串叫“魔法”字符串。解決方法就是定義常量。
const TYPE_ADD="TYPE_ADD"
const TYPE_DEC="TYPE_DEC"
同理,產生事件也變成一個方法:
function createAddAction(value){
return {type:TYPE_ADD,payload:value}
}
combineReducers 合併多個reducer
如果有多個狀態內容,比如user 的狀態,post 的狀態,如果都寫在一個文件裏,會太龐大。所以,用combineReducers這個方法合併
function userReducer(state, action) {
//省略
}
function postReducer(state, action) {
//省略
}
const reducer = combineReducers({user: userReducer, post: postReducer})
這樣產生出來的state 就象:
{
user:{}
post:{}
}
react-redux 粘合劑
redux 與react 結合時,不用 react-redux 也可工作。狀態改變後,用store.subscriber 通知react重渲染
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
const subscriber = () => {
ReactDOM.render(
<div>
<div>{store.getState().value}</div>
<button onClick={() => store.dispatch({type: 'add', payload: 1})}>加1</button>
<button onClick={() => store.dispatch({type: 'dec', payload: 1})}>減1</button>
</div>,
document.getElementById('root')!
)
};
store.subscribe(subscriber)
這樣寫,主要的缺陷就是組件與store 耦合緊密,不利於調試與複用,所以就用到react-redux 來解耦與粘合。
Provider 與 connect
主要就兩個內容:Provider 組件與 connect 方法。 Provider 用來提供全局的狀態提供者,connect 將普通Component包裹一下,能夠接收Provider提供的狀態。
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
import {Provider,connect} from 'react-redux'
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
//以下開始不一樣
class Counter extends Components{
render(){
<div>
<div>{this.props.value}</div>
<button onClick={this.props.addMethod}>加1</button>
<button onClick={this.props.decMethod}>減1</button>
</div>
}
}
function mapStateToProps(state){
return {
value:state.value
}
}
function mapDispatchToProps(dispatch){
return {
addMethod:()=>dispatch({type: 'add', payload: 1})
decMethod:()=>dispatch({type: 'dec', payload: 1})
}
}
const WrapedCounter=connect(mapStateToProps,mapDispatchToProps)(Counter)
ReactDOM.render(
(
<Provider store={store}>
<WrapedCounter/>
</Provider>
),
document.getElementById('root')
)
connect 方法需要兩個函數參數,一個是mapStateToProps ,把store中的狀態映射到組件的屬性。一個是mapDispatchToProps,把發送事件的方法映射成組件的屬性。
總結
謝謝閱讀