The premise
Complex scenarios have a lot of data that needs to be used and modified back and forth between different pages. But the direct data communication mode of applets page is very simple. It is often necessary to maintain a global object to hold common data. However, simply maintaining a common data entity can become too large as the business logic becomes increasingly complex, and changes to the data are often not well traceable. In addition, there is no good synchronization between the modification of data in the public data entity and the UI of the page, so it is often necessary to maintain the same data in the page and the corresponding data entity at the same time, which makes the operation very inconvenient.
Taro has previously used the structure of React + Redux to develop wechat small programs, and redux can solve the above problems as a whole. But Taro also has some potential problems that are unacceptable. In line with the principle of never using third party secondary encapsulation libraries if you can use native. I’ve always wanted to try redux in native wechat applet development.
Problems that need to be solved
1. Access to redux library 2. Binding of page UI and Redux dataCopy the code
Introduction of the Redux library
1. Install REdux using either NPM or YARN.
Specific to the story in Chinese website is as follows: www.reduxjs.cn/introductio…
2. Wechat applets introduce external NPM packages.
Miniprogram_npm is generated by using micro program IDEA and Build NPM in tools.
Redux library ReferenceError: process is not defined
Because of the wechat applets Build NPM tool, the nodeProcess environment variable is not introduced in the Build, but redux optimizes different envs accordingly. As a result, the built package is missing the process variable. The easiest solution is to inject the required processes yourself in the completed build package.
This basically solves the problem of missing process parameters encountered by all third-party libraries. If you need to manually modify after each run of the Build NPM tool. If you have multiple third-party libraries that need to be manually modified, that can be troublesome. Therefore, it is necessary to complete dynamic changes through scripts, using tools such as AST trees, to save labor costs (this is covered later).
In summary, the introduction of Redux is complete.
Add Redux to your project
1. Store creation
Merge different entities using combineReducers and createStore entities using createStore and export them. For data consistency, redux’s principle is that only one store is initialized per project, so any subsequent operations are performed in the currently generated store.
Merge data entities:
const { combineReducers } = require('redux');
const testItem = require('./testItem/index');
const testItem2 = require('./testItem2/index');
const user = require('./user/index');
const reducer = combineReducers({
testItem: testItem.testItem,
testItem2,
user
});
module.exports = {
reducer
}
Copy the code
Export the store:
const { createStore, applyMiddleware } = require('redux');
const { reducer } = require('./reducers');
const { logger } = require('redux-logger');
const store = createStore(
reducer,
applyMiddleware(logger)
)
module.exports = {
store
}
Copy the code
2. Maintain store globally
This is different from react. Wechat applet does not have corresponding controls to maintain store globally, so WHAT I do is to maintain store directly in app.js globalData, so that every page can directly access the Store
app.js:
const { store } = require('./redux/index');
//app.js
App({
globalData: {
$store: store,
getState: ()=> store.getState(),
}
})
Copy the code
Simulate connect method
In React, the Connect method is implemented through higher-order components, but this method is not suitable for wechat applets. Fortunately, Redux provides a subscribe method that listens for changes in the store. So the preliminary design:
When the page is included or displayed, add listener, and destroy listener when the page is hidden or destroyed. 2. After adding listener, simulate mapState method to inject data in corresponding REDUx into data of the page. 4. Simulate mapDispatch method to provide the method of modifying store data for the pageCopy the code
pageW.js:
const { store } = require('.. /redux/index'); const initPage = (params = {}, connect = []) => { const { onLoad = ()=>{}, onShow = ()=>{}, onHide = ()=>{}, onUnload = ()=>{}, data = {} } = params; const newPage = { ... params, // ---------------- OnLoad(... p) { onLoad.bind(this)(... p); }, OnShow(... p) { onShow.bind(this)(... p); }, OnHide(... p) { onHide.bind(this)(... p); }, OnUnload(... p) { onUnload.bind(this)(... p); }, / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / empty listening clearStoreSubscribe () {if (this. StoreSubscribe) {enclosing storeSubscribe (); this.storeSubscribe = undefined; }}, // get redux data getNewData() {const newItems = {}; const state = this.$store.getState(); if (connect) { if ( Array.isArray(connect) ) { connect.forEach((key) => { const value = state[key]; if (value && this.data[key] ! == value) { newItems[key] = value } }) } else if (typeof connect === 'function') { const list = connect(state) || {}; Object.keys(list).forEach((key) => { const value = list[key]; if (value && this.data[key] ! == value) { newItems[key] = value } }) } } return newItems; }, // Listen for redux changes handleReduxChange() {this.setData({... this.getNewData(), }); }, // ---------------- data: { ... data }, onLoad(... p) { const app = getApp() this.$store = app.globalData.$store; this.setData({ ... this.getNewData(), }); this.OnLoad(... p); this._isOnLoad = true; }, onShow (... p) { if (! this.storeSubscribe) { this.storeSubscribe = this.$store.subscribe(()=>this.handleReduxChange()); } if (! this._isOnLoad) { this.setData({ ... this.getNewData(), }); } this.OnShow(... p); this._isOnLoad = false; }, onHide(... p) { this.OnHide(... p); this.clearStoreSubscribe(); }, onUnload(... p) { this.OnUnload(... p); this.clearStoreSubscribe(); }, // ---------------- dispatch(... p) { if (this.$store) { return this.$store.dispatch(... p); } } } return newPage; } const PageW = (params = {}, mapState = [], mapDispatch = ()=>{}) => { const page = initPage({... params}, mapState); const dispatchList = mapDispatch(store) || {}; page.mapDispatch = { ... dispatchList }; return Page(page); } module.exports = PageW;Copy the code
The main considerations and deficiencies in PageW are as follows:
1. In order to keep the original life cycle name of wechat applet unchanged, we hijacked the life cycle of the incoming page in advance, and then used bind to trigger it again after the corresponding life cycle is completed. When redux updates data, it will generate a new data object, so every time it listens to data changes, the new data and the old data will be compared, and every time setData, only put the data that has really changed. Redux Connect data has been added, but currently there is no safe distinction between the two data names, so the data names in the page's native data must be different from the data injected by CONNECT.Copy the code
Test page:
The two data testItem and testItem2 are imported, and the add2 method is imported
const PageW = require('.. /.. /pageW/index'); const { ActionsFun } = require('.. /.. /redux/testItem/actions'); const page = { data: { wwj: 4 }, onLoad() { console.log('sub onLoad'); }, onShow() { }, toTest() { console.log('toTest'); wx.navigateTo({ url: '/pages/test/index' }) }, button1() { console.log('button1'); this.mapDispatch.add2(); }, button2() { const { wwj } = this.data; this.setData({ wwj: wwj + 2 }); }, } const mapState = [ 'testItem', 'testItem2' ]; const mapDispatch = ({dispatch}) => { return { add2: (params) => dispatch(ActionsFun.add(params)) } } PageW(page, mapState, mapDispatch);Copy the code