React implements Redux asynchronous action – web and native, Redux isomorphism (1)

Time:2021-11-26

Recently, I made a webapp in the company, using react and redux

Then, the company wants to reconstruct the previous app with react native, and reuse it with webapp code (mainly Redux code). The interface is web and native

Project structure issues

Sub module

Now the sub module function of Git is used, which is equivalent to two projects sharing a common directory. The directory structure is as follows (the common directory is a sub module)

#Web project

root
--Client // Web project code
----LIBS // self written modules
------Storage.js // Web data module

--Common // sub module
----IMGs // picture directory
----Redux // Redux directory
------actions
------data
------reducers


#React native project

root
--Client // Web project code
----LIBS // self written modules
------Storage.js // native data module

--Common // sub module
----IMGs // picture directory
----Redux // Redux directory
------actions
------data
------reducers

Babel configuration

Web side

# .babelrc
{
    "presets": ["react", "es2015", "stage-3"]
}

You should also import at the entry filebabel-polyfill

# index.js
import 'babel-polyfill'

Native end

# .babelrc
{
    //This is compatible with async function
    "presets": ["react-native-stage-0"]
}

Redux problem

Redux library used

import promiseMiddleware from 'redux-promise';
import axiosMiddleware from 'redux-axios-middleware';

const enhancers = compose(
    applyMiddleware(
        Promisemi ddleware, // asynchronous action Middleware
        Axiosmiddleware (Axios), // Axios Middleware
    )
);

action

export function messageList() {
    let local = localStorage.getItem('timestamp') || '0'
    
    return {
        type: 'MESSAGE_LIST',
        payload: {
            request: {
                method: 'get',
                url: '[url]' + '?timestamp=' + local
            },
        }
    }
}

When writing to the web side, I used to get the data in the local storage in aciton, but I ran into a problem in react native. Because there was no way to get the local data synchronously in react native, the previous code could not be reused. Redux promise was introduced to solve the problem of asynchronous action, It can make the return value of the action promise, and then encapsulate the local storage, so that both ends can use a set of code. The code is as follows (the web also needs to be encapsulated, but async is not used)

import storage from '../../../client/libs/storage.js' // 
export async function messageList() {
    let local = await storage.getItem('timestamp')
    
    return {
        type: 'MESSAGE_LIST',
        payload: {
            request: {
                method: 'get',
                url: '[url]' + '?timestamp=' + local
            },
        }
    }
}


# storage.js native
import { AsyncStorage } from 'react-native'

export default {
  getItem : async function (key){
    let value = await AsyncStorageGetItem(key)
    return value
  }
}

async function AsyncStorageGetItem(key) {
  return new Promise((resolve,reject)=>{
    AsyncStorage.getItem(key, function (value) {
      resolve(value)
    })
  })
}
# storage.js native end


# storage.js web
export default {
  getItem : function (key){
    return JSON.parse(localStorage.getItem(key))
  }
}
# storage.js web end 

reducers

At present, there is no solution to use asynchronous operations in reducers, so we can only skillfully pass in the local data needed in reducers during actions. As shown below, aciton.meta.previousaction is the of the Redux Axios middleware library. See me for detailsThis article

export async function messageList() {
    let local = await storage.getItem('timestamp')
    let reducersData = await storage.getItem('[key]')
    
    return {
        type: 'MESSAGE_LIST',
        payload: {
            request: {
                method: 'get',
                url: '[url]' + '?timestamp=' + local
            },
        },
        reducersData: reducersData
    }
}


case 'MESSAGE_LIST':
    return state;
case 'MESSAGE_LIST_SUCCESS':
    //Get data from previous action
    let previousActionData = aciton.meta.previousAction.reducersData
    
    return state;
case 'MESSAGE_LIST_FAIL':
    return state;

data

Some default values are generally defined in data, but if you want to get data from local and assign values again, there is no good method to be solved

{
    //It can be written like this in the web, which is difficult to implement in native
    timestamp: localStorage.getItem('approvalListData.timestamp') || { }
}

These problems are encountered at present, and they will be updated later~