Redux作為一個應用狀態(tài)管理工具,自身和應用框架沒有耦合。理論上我們可以將Redux應用到任何應用中,這也是本文標題Redux Anywhere的由來。本文簡單分析React應用中使用Redux的原理,同時整理了如何在微信小程序中使用Redux。
1.React中使用Redux
1.1 使用示例
在React中使用Redux,需要使用React-Redux工具。這一工具為我們提供了兩個組件
- Provider.js
- connect.js
當我們在React中使用Redux時,首先將頂層組件用Provider包裹
var store = createStore(reducer);
var Provider = require('react-redux').Provider;
// App 為上層的Component
class App extend React.Component{
render() {
return (
<Provier store={store}>
<Container />
</Provider>
);
}
}
在組件中,通過connect方法,返回一個被包裹的組件
var connect = require('react-redux').connect;
var actionCreators = require('...');
// MyComponent是與redux無關的組件
var MyComponent = require('...');
function select(state) {
return {
count: state.count
}
}
export default connect(select, actionCreators)(MyComponent)
1.2 原理簡析
Provider主要代碼
export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return Children.only(this.props.children)
}
}
React中有一個全局的context對象,各級組件間可以通過context通信。
通過Provider代碼可知,這里實際是將Redux store對象綁定到React的context中,以此來把store應用到React中。
下列片段將React 組件用Provider組件包裹,傳入了Redux store,返回一個高階組件。高階組件在渲染時,以包裹的子組件渲染。
<Provier store={store}>
<Container />
</Provider>
connect.js主要邏輯
connect是一個高階函數,執(zhí)行connect(select, actionCreators)(MyComponent)時,生產出一個經過包裹的Connect組件,該組件具有如下特點:
- 通過this.context獲取store
- props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作為props傳給真正的Component
- componentDidMount時,添加事件this.store.subscribe(this.handleChange),使得Redux dispatch(action)后,觸發(fā)回調
- shouldComponentUpdate時判斷是否有避免進行渲染,提升頁面性能,并得到nextState
- componentWillUnmount時移除注冊的事件this.handleChange
- 在非生產環(huán)境下,帶有熱重載功能
這里的關鍵點就在于,通過connect包裹的業(yè)務組件,向Redux store注冊了監(jiān)聽器。在Redux流程中,dispatch(action)時,會檢查當前store的監(jiān)聽器并依次觸發(fā),這樣,就將React組件與Redux結合了起來。
connect.js代碼較多,這里不貼出。注釋版代碼可參看 react-redux源碼學習筆記
2.微信小程序中使用Redux
在微信小程序開發(fā)中,有一個全局的app.js,應用其他地方可以通過getApp()獲得其引用。
結合React-Redux經驗,不難想到,可以將Redux store綁定到全局的app.js中
state變更后,如何觸發(fā)視圖的刷新呢?與connect()方法中關鍵點類似,在page.js中,同樣為Redux store注冊監(jiān)聽器。這樣,在dispatch(action)后,觸發(fā)回調,刷新數據,重新渲染視圖
示例如下
// actions.js
export const changeText = (text) => {
return { type: 'CHANGE_TEXT', text }
}
// reducers.js
export default {
myText(state = '', action) {
switch (action.type) {
case 'CHANGE_TEXT':
return action.text
default:
return state
}
}
}
//app.js中初始化Store,并放在App實例里以方便調用
//app.js
import { createStore, combineReducers } from './lib/redux.min'
import reducers from './reducers'
const Store = createStore(combineReducers(reducers))
App({
Store,
onLaunch: function () {
// ...
//page logs.js
const app = getApp()
const Store = app.Store
const dispatch = Store.dispatch
Page({
data: {
foo: ''
},
onLoad() {
this.unsubStore = Store.subscribe(() => {
const foo = Store.getState().myText
this.setData({ foo })
})
},
onUnload() {
this.unsubStore()
},
bindBtn() {
dispatch(changeText('new text'))
},
})
上述代碼中,當觸發(fā)dispatch(changeText('new text'))時,在Redux store dispatch方法中,會依次觸發(fā)各個監(jiān)聽器,從而完成處理流程。
上述是一個簡單的示例,無法實現React-Redux那樣,通過connect方法對組件的包裝。定制業(yè)務組件關注的state,并根據新舊state判斷是否需要刷新視圖。
這個wechat-weapp-redux 組件,基于React-Redux進行了微信小程序的改造,可以滿足上述需求。
3.Redux Anywhere
至此,我們已經明白了在應用中使用Redux的關鍵點
- 將store綁定到一個單例全局對象中
- 業(yè)務組件中注冊監(jiān)聽器,回調中處理數據更新邏輯
理解上述兩點,便可以將Redux應用到任何應用中了。
參考文章
如果覺得有幫助,可以掃描二維碼對我打賞,謝謝
