All State In Redux
In the previous article, we explained how to use Redux/Redux-Saga to share component state and manage side effects.
In subsequent development, all of our pages and business logic components used this set of development patterns. For example, we have an App search AutoComplete component that does the following:
- Reads the user token from Redux’s State.
- Through the user’s token, the user requests the background service to obtain the list of apps that the user has the permission to see.
- The App information is loaded into the component and corresponding search terms are returned according to user input.
Since this component needs to read user tokens stored in Redux State and contains asynchronous requests, it is appropriate to manage its State in Redux and use Redux-Saga to handle asynchronous requests.
Component reuse
However, in terms of component reusability, we have a problem. Since Redux itself does not provide modularity, when we want to reuse components using Redux/Redux-Saga:
- We need to register a globally non-conflicting reducerKey for this component.
- We need to change the type of action the component is interested in, because there are multiple component instances globally, and duplicate action types can cause incorrect component state changes.
- After the components have been umounded, we need to manually clear the app list information for the components as the state saved in the Redux will not be destroyed automatically.
- After the components are uninstalled, the Reducer continues to exist, slightly reducing some performance.
For this situation, we developed Redux-Arena. Redux-arena will export the Redux/Redux-Saga code with the React component as a React high-order component for reuse:
- When a higher-order component is mounted, the task of Redux-saga is automatically initialized, the reducer of the component is initialized, and its own node is registered on the State maintained by Redux.
- When a higher-order component is uninstalled (Unmout), the redux-saga task is automatically cancelled, the reducer of the component is destroyed, and its own node is deleted from the State maintained by Redux.
- Provides a component channel mechanism. By default, actions sent by a component can only be received by the Reducer of the component. You can also configure the channel to be discarded to receive global actions.
- Provide vReducerKey mechanism. In Redux, if you want to share state information among components, you need to know the real node name, which is easy to cause conflicts in reusable Redux components. Redux-arena provides vReducerKey mechanism to ensure that the real name of state node will never conflict. VReducerKey If it has the same name, the lower component overwrites the vReducerKey information of the upper component.
- Provides a one-way (like Flux’s one-way data flow) sharing scheme for component states and actions, and lower-level components can get state and Actions for upper-level components through vReducerKey.
- Deep integration with Redux-Saga, you can also choose to send and receive only the component’s own actions in Redux-Saga.
Constructing reusable higher-order components (Scenes)
We call each exported high-order component of the Redux/React binding Scene. First we create actions and reducer for the Scene, then use the bundleToComponent to export the higher-order components.
import { bundleToComponent } from "redux-arena/helper";
import * as actions from "./actions";
import state from "./actions";
import reducer from "./reducer";
import ComponentA from "./ComponentA";
export default bundleToComponent({
Component: ComponentA,
actions,
state,
reducer
});
Copy the code
The exported component can now be used in React as a normal component.
Note that redux-arena enhances Redux’s Store by creating aStore with createArenaStore:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createArenaStore } from "redux-arena";
import ComponentA from "./ComponentA";// The higher-order component exported above
const store = createArenaStore();
const app = document.getElementById("app");
ReactDOM.render(
<Provider store={store}>
<ComponentA />
</Provider>,
app
);
Copy the code
Very simple.
Communication isolation
By default, each Scene accepts only the actions it sends. Any other Scene or action bound to a native Redux will be ignored.
Here’s an example:
import { bundleToComponent } from "redux-arena/helper";
import * as actions from "./actions";
import reducer from "./reducer";
import ComponentA from "./ComponentA";
export const ComponentA1 = bundleToComponent({
Component: ComponentA,
actions,
reducer
});
export const ComponentA2 = bundleToComponent({
Component: ComponentA,
actions,
reducer
});
Copy the code
The code sources of ComponentA1 and ComponentA2 are the same, but their Reducer will only receive the actions sent from its own Scene, and the actions sent by other components will be ignored even if they have the same type.
State is shared with Actions
In native Redux, if you want to achieve the sharing of state, you need to register a globally unique reducerKey for the component, and then pass the corresponding state into props using the mapStateToProps method.
Redux-arena uses Vitural ReducerKey (vReducerKey), which does not require global uniqueness. When the vReducerKey of the child component is the same as that of the parent component, The child component’s vReducerKey overwrites the parent component’s vReducerKey.
Specify the vReducerKey for the Scene:
import { bundleToComponent } from "redux-arena/helper";
import * as actions from "./actions";
import reducer from "./reducer";
import ComponentA from "./ComponentA";
export default bundleToComponent({
Component: ComponentA,
actions,
reducer,
options: {
vReducerKey: "a1"}});Copy the code
Like mapStateToProps, the subcomponent uses propsPicker to pull out the required state and actions:
import { bundleToComponent } from "redux-arena/helper";
import state from "./state";
import actions from "./actions";
import ComponentAChild from "./ComponentAChild";
export default bundleToComponent({
Component: ComponentA,
state,
actions,
propsPicker:(state,actions,allState,{ a1 }) = >({
name: state.name, / / the Scene of the state
actions, / / Scene actions
a1Name: allState[a1.reducerKey].name, / / component of the state
a1Actions: a1.actions / / component of the actions})});Copy the code
This allows us to share state and actions between components.
It should be noted that this sharing mode is similar to the one-way data flow of Flux, and the transmission direction is one-way. In reverse information transmission, only action can be used instead of state.
If state is shared between sibling components, you need to add a data layer on one of the parent components between these sibling components and use this unified parent data layer to share state.
The integration of story – Saga
Redux-arena provides a series of redux-saga methods for communication isolation, allowing redux-Saga to receive only the actions from the current Scene.
Use setSceneState to easily set the State of the current Scene.
import { setSceneState, takeLatestSceneAction } from "redux-arena/sagaOps";
function * doSomthing({ payload }){
yield* setSceneState({ payload })
}
export function* saga (){
yield takeLatestSceneAction("DO_SOMETHING", doSomthing)
}
Copy the code
Redux-arena Github address: github.com/hapood/redu… For specific use, please refer to the example in the project directory. Documents are being completed successively.