Detailed explanation of the use of redux in native WeChat applet development

Detailed explanation of the use of redux in native WeChat applet development

premise

In complex scenarios, a lot of data needs to be used and modified back and forth between multiple different pages. However, the direct data communication method of the mini program page is very simple. Usually you need to maintain a global object to store common data. However, simply maintaining a shared data entity will become too large as business logic becomes increasingly complex, and data modifications often cannot be well traced. In addition, there is no good synchronization method between the modification of data in the public data entity and the UI of the page. It is often necessary to maintain the same data in both the page and the corresponding data entity, which is very inconvenient.

Taro has been used before to develop WeChat applets with the structure of react+redux. Relying on redux can solve the above problems as a whole. But Taro itself also has some unacceptable potential problems. In line with the principle of never using third-party re-packaged libraries when native ones can be used. I have always wanted to try to integrate redux in the development of native WeChat applet.

Problems to be solved

1. Access to the redux library
2. Binding of page UI and redux data

Introduction of redux library

1. To install redux, you can use npm or yarn.
The specific redux Chinese official website is as follows: https://www.reduxjs.cn/introduction/getting-started/
2. WeChat applet introduces external npm packages.
Use WeChat Mini Program IDEA, Build npm in tools, and generate miniprogram_npm.

3. Solution to the error ReferenceError: process is not defined in the redux library.

Because WeChat applet Build npm tool, nodeprocess environment variables will not be introduced during construction, but redux has made corresponding optimizations for different envs. As a result, the built package lacks process variables. The most convenient solution is to inject the required process into the built package yourself.

This can basically solve the problem of missing process parameters encountered by all third-party libraries. If you need to modify it manually every time you run the Build npm tool. It would be troublesome if there are multiple third-party libraries that need to be modified manually. Therefore, it is necessary to use scripts and tools such as ast trees to complete dynamic modifications and save labor costs (this will be introduced later)

In summary, the introduction of redux is complete.

Add redux to your project

1. Store creation

Use combineReducers to merge different entities, use createStore to create the store entity, and export it. For the sake of data uniformity, the principle of redux is that a project only initializes one store, 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
}

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
}

2. Global maintenance store

The usage here is different from that in react. WeChat applet does not have a corresponding control to globally maintain the store, so my approach is to maintain it directly in the globalData of app.js, so that each page can directly obtain the store
app.js:

const { store } = require('./redux/index');

//app.js
App({
 globalData: {
  $store: store,
  getState: ()=> store.getState(),
 }
})

Simulate the connect method

In react, the connect method is implemented through high-order components, but this method is not applicable to WeChat applets. Fortunately, redux provides a subscribe method to monitor changes in data in the store. So the initial design:
1. Whenever a page is entered or displayed, add a listener, and destroy the listener when the page is hidden or destroyed
2. After adding the listener, simulate the mapState method and inject the corresponding data in redux into the data of the page
3. When monitoring data changes in redux, update the page data to refresh the page UI
4. Simulate the mapDispatch method to provide a method for the page to modify store data

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);
  },
  // ----------------
  // Clear listener clearStoreSubscribe() {
   if (this.storeSubscribe) {
    this.storeSubscribe();
    this.storeSubscribe = undefined;
   }
  },
  // Get data in redux
  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;

The main considerations and deficiencies in PageW are as follows:
1. In order to keep the original life cycle name of the WeChat applet unchanged, the life cycle of the incoming page is hijacked in advance, and then bind is used to trigger it again after the corresponding life cycle is completed.
2. Because redux updates data, a new data object will be generated. Therefore, whenever data changes are detected, the new data and the old data will be compared. Each time setData is called, only the data that has actually changed will be put in.
3. The data in the page not only maintains the data created by the default page, but also adds the data after redux connect. However, there is currently no safe distinction between the names of these two data, so the data name in the page's native data must be different from the data injected by connect.

Test page:

Imported two data items, testItem and testItem2, and imported a method called add2

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);

This is the end of this article about the detailed use of redux in the development of native WeChat applet. For more relevant content on the use of redux in applet, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • How to integrate redux/immutable/thunk third-party libraries in mini programs
  • WeChat applet Redux binding example detailed explanation

<<:  Install Mininet from source code on Ubuntu 16.04

>>:  Solve the problem that Mysql5.7.17 fails to install and start under Windows

Recommend

Docker implements cross-host container communication based on macvlan

Find two test machines: [root@docker1 centos_zabb...

Monitor changes in MySQL table content and enable MySQL binlog

Preface binlog is a binary log file, which record...

Vue integrates PDF.js to implement PDF preview and add watermark steps

Table of contents Achieve results Available plugi...

Analysis of common usage examples of MySQL process functions

This article uses examples to illustrate the comm...

Solution to the data asymmetry problem between MySQL and Elasticsearch

Solution to the data asymmetry problem between My...

Pure JS method to export table to excel

html <div > <button type="button&qu...

Analysis of the process of implementing Nginx+Tomcat cluster under Windwos

Introduction: Nginx (pronounced the same as engin...

Docker container exits after running (how to keep running)

Phenomenon Start the Docker container docker run ...

Automatic backup of MySQL database using shell script

Automatic backup of MySQL database using shell sc...

Implementation of Vue 3.x project based on Vite2.x

Creating a Vue 3.x Project npm init @vitejs/app m...

A brief discussion on CSS cascading mechanism

Why does CSS have a cascading mechanism? Because ...