React & Redux Application Architecture
official_dulin
Posted on August 5, 2021
Architecture based on React Hooks and React FC design:
View layer
React functional component to build the view, containing :
- ReactElement, the JSX view element
- Event handling functions for the view, such as onClick, etc.
- Use the hooks provided by the controller layer to get the View Model
The view logic using the internal state of the component is encapsulated by a custom hook that exports the state and the functions to manipulate the state, and the event handling functions directly call the custom hook exported functions to change the state of the view.
Controller layer
Mainly using React hooks to implement, including
- Business custom hooks
- UI custom hooks
UI custom hooks encapsulate the internal state of the component (defined by useState
) and its change operations, the internal state of the component may depend on the component's props calculated by logic, all encapsulated in hooks, this piece of code logic should not be placed in the component.
Business custom hooks encapsulate data and its operations related to business logic, data sources include backend service API call returns, web storage, cookies, constants, URL query parameter, etc. Need to persist data to the redux store data acquisition method using dispatch + redux-thunk created asynchronous action creator (redux-saga, etc.), considering that some views are very independent and do not need to persist API data to the redux store, you can omit dispatch + async action creator, directly call the front-end fetch wrapped API service directly to call the backend service API.
The data generated by user-view interaction may be persisted in the redux store, typically data such as filter conditions, obtained through useSelector
+selector, with this redux state corresponding redux action operations are also encapsulated in the hook, through useDispatch
+action creator for operations.
Data Access layer
Contains.
- Selector created by Reselect library, used to read data from redux store and calculate derived data
- Redux thunk (redux-saga) and other middleware created by thunk or saga, used for asynchronous process control, action meta data processing, call front-end API service, incoming verification and processing, to ensure that the parameters passed to the API service method is correct.
Use the createSelector method provided by the reselect library to create a selector as a method to access the redux store. selector can be used either by useSelector
or in redux-thunk by xxxSelector(getState()
. which is used to get a certain state slice on the redux store.
Another purpose of selector is to provide optimization for computing derived data. selector can compute derived data based on the component's props and state, Accessing React Props in Selectors, which can calculate derived data based on dynamic or non-dynamic arguments How do I create a selector that takes an argument?, the selector provides a memozie function that returns the result of the last calculation (equal references, equal values) with the same input, in conjunction with React.memo
, useEffect
's dependency list skips the effect
and uses useMemo
. If the dependency list uses the derived data returned by the selector, the memorized result can be created while the reference and value of the returned result remain unchanged, avoiding the component to render re-executes expensive logic, completes the rendering optimization of the component, and reduces unnecessary re-render.
Translated with www.DeepL.com/Translator (free version)
Service layer
A relatively broad category containing helper, utils, third-party libraries, generic custom hooks, third-party hooks, etc. dedicated to a specific task.
The main function is to interface to external data sources, backend API service, third-party APIs, websockets, etc. The communication protocol is mainly HTTP protocal, and the preprocessing of requests is done through interceptors. Pre-processing of requests, pre-processing of responses and error handling. No matter what external data source interface is called, the data structure of the output of the front-end API service should be fixed by a unified standard (pre-defined interface), for example, the output object contains three fields: {error: null, result: null, message: null}
.
helper, utils store generic methods, do not care about and should not contain business logic, not to repeat.
The API service methods can be called in the controller layer hooks or in the async action creator created by redux thunk, not directly in the component view layer.
Data Persistence layer
The data stored in the Redux store is not considered persistent in the strict sense, as it is stored in the application memory and belongs to Memory DB, the life cycle is the life cycle of the application, the application is initialized (refresh the browser, start, restart the service), then the previously stored data is lost. Depending on the requirements decide whether to use libraries such as redux-presist to persist the data in Redux store to Web Storage.
The main types of data stored are as follows.
- Business data from external data sources
- Data generated by user interaction with the View layer, such as forms, filter conditions, etc.
- Data from Web Storage and cookies to initialize the redux store, depending on the requirements
Other data sources that the application depends on: Web Storage, cookies, URL query parameter, application-defined constants, etc. for the browser environment.
The specific architecture is adjusted according to the requirements, and the separation of concerns is achieved through layering, partitioning, etc. Combined with componentization, modularization, high cohesion, low coupling, TDD to improve the quality of front-end code, improve readability, maintainability, scalability, reusability.
Additional: components are divided into display components and container components, and container components can be subdivided into page level, component level, and according to the scope of the role can also be divided into page level, component level, it is customary to create hooks.ts in the directory where the component files are located to store the custom hooks needed for that level of components. The larger the scope, the more generic the hooks are, and the closer the files are to the root directory.
Translated with www.DeepL.com/Translator (free version)
Original article link: https://github.com/mrdulin/blog/issues/95
Posted on August 5, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.