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.
models
The incomingmodels
checkreduxConfig
The configuration for redux is passed in later. I just changed the nameforEachPlugin
The equivalent ofArray.prototype.find
Retrieve 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.