This is the 16th day of my participation in the August Text Challenge.More challenges in August

preface

Those familiar with React will be familiar with Redux. Rematch is an alternative to Redux as long as state management is required.

React solves many problems, such as too much template code, the same operation corresponding variables, processing methods scattered in multiple pages, and so on.

Let’s start by looking at how Rematch is used.

For example

import { init } from '@rematch/core'
import * as models from './models'

const count = {
    state: 0.reducers: { 
        increment(state, payload) {
            return state + payload
        }
    },
    effects: {
        async incrementAsync(payload, rootState) {
            await new Promise(resolve= > setTimeout(resolve, 1000))
            this.increment(payload)
        }
    }
}

const store = init({
    models: {
        count,
    },
})

export default store
Copy the code

As you can see, in Redux you need to create action.js, state.js, reducer.js, but Rematch allows you to combine the three together, and this set of units is called model.

It is worth mentioning that Rematch also requires Redux, which can be understood as an extension of Redux.

Also, since Rematch is developed in Typescript, the Typescript types are left out for ease of explanation.

Here’s what Rematch does.

init

The above example uses only the init API.

export const init = (initConfig) = > {
    const config = createConfig(initConfig || {})
    return createRematchStore(config)
}
Copy the code

The configuration passed into init will first be passed to createConfig and then createRematchStore will be called.

function createConfig(initConfig) {
    const storeName = initConfig.name ?? `Rematch Store ${count}`
    count += 1
    const config = {
        name: storeName,
        models: initConfig.models || {},
        plugins: initConfig.plugins || [],
        redux: {
            reducers: {},
            rootReducers: {},
            enhancers: [].middlewares: [],
            ...initConfig.redux,
            devtoolOptions: {
                name: storeName, ... (initConfig.redux? .devtoolOptions ?? {}), }, }, } validateConfig(config) config.plugins.forEach((plugin) = > {
        if (plugin.config) {
            config.models = merge(config.models, plugin.config.models)
            if (plugin.config.redux) {
                config.redux.initialState = merge(
                    config.redux.initialState,
                    plugin.config.redux.initialState
                )
                // ...
            }
            validatePlugin(plugin)
        }
    })
    
    return config
}
Copy the code

Here we create an incremented storeName, then split all the incoming initConfig objects into config, and all subsequent operations are processed on this object.

Config.redux is then passed to Redux as an initialization parameter for Redux, since Rematch is implemented on Redux.

Then execute validateConfig and validatePlugin respectively. Both validate functions follow the same pattern.

function validatePlugin() {
    validate(() = > [
        [
            !ifDefinedIsFunction(plugin.onStoreCreated),
            'Plugin onStoreCreated must be a function',
        ],
        [
            !ifDefinedIsFunction(plugin.onModel), 
            'Plugin onModel must be a function',].// ...])}function validate(runValidations) {
    if(process.env.NODE_ENV ! = ='production') {
        const validations = runValidations()
        const errors: string[] = []

        validations.forEach((validation) = > {
            const isInvalid = validation[0]
            const errorMessage = validation[1]
            if (isInvalid) {
                errors.push(errorMessage)
            }
        })

        if (errors.length > 0) {
            throw new Error(errors.join(', '))}}}Copy the code

As you can see, the validate function is extracted, which iterates through the array to see if the first item of the array is true, and throws an error message for the second item of the data if it is not true.

ValidateConfig and validatePlugin can then be implemented using the validate method:

  • Verify a variety of abnormal parameters
  • No redundant judgment, more concise, elegant

createRematchStore

After the above formatting of initConfig and verification of the exception passing format, enter the body.

function createRematchStore(config) {
    const bag = createRematchBag(config)
    bag.reduxConfig.middlewares.push(createEffectsMiddleware(bag))

    bag.forEachPlugin('createMiddleware'.(createMiddleware) = > {
        bag.reduxConfig.middlewares.push(createMiddleware(bag))
    })
    const reduxStore = createReduxStore(bag)
    // ...
}
Copy the code

The formatted config is first passed to createRematchBag.

function createRematchBag(config) {
    return {
        models: createNamesModels(config.models),
        reduxConfig: config.redux,
        forEachPlugin(method, fn) {
            config.plugins.forEach(plugin= > {
                if (plugin[method]) {
                    fn(plugin[method])
                }
            })
        }
    }
}
Copy the code

CreateRematchBag returns an object containing Models, reduxConfig, and forEachPlugin.

  • modelsThe incomingmodelscheck
  • reduxConfigThe configuration for redux is passed in later. I just changed the name
  • forEachPluginThe equivalent ofArray.prototype.findRetrieve the Method Plugin and pass in the callback function represented by the second parameter.

CreateEffectsMiddleware is then executed. In Rematch Effects is similar to the action in Redux + redux-thunk that returns functions.

function createEffectsMiddleware(bag) {
    return store= > next= > action= > {
        if (action.type in bag.effects) {
            next(action)
            
            return bag.effects[action.type](
                action.payload,
                store.getState(),
                action.meta,
            )
        }
        
        return next(action)
    }
}
Copy the code

As you can see, createEffectsMiddleware is an Effects Redux middleware. The first three function arguments, store, Next, and action, represent:

  • Story of the store
  • Redux of dispatch
  • Redux of the action

Action. Type matches any of the effects in the Rematch.

If the match is matched, next(Action) is first executed. Even if the effects of Rematch are matched, the reducer that may exist in the following reducer will be continued.

It then executes the corresponding Rematch effect and passes in three parameters: action.payload, store state, and action.meta.

About the action. Meta

Action. meta is a parameter added to extend the action.

See: redux-toolkit.js.org/api/createA…

summary

This article describes Rematch formatting the incoming initConfig and implementing an Effect middleware with createEffectsMiddleware.