There are many ways to manage our state in frontend, but in my opinion (after debugging websites which use react), state can be classified into three categories:
- Domain state: data that we get from our servers (list of invoices, customers, signalR ...).
- App state: data that we use for specific components (will explain details in the section below).
- UI state: data that represents how UI is displayed.
I think best way to explain this concept is via example.
Our website has invoice create/edit page /:company/invoices-beta/create (image below). In this scene, we have few data which need to be shared across 4 blocks like:
- Current selected customer.
- invoice empty data.
- Number of invoice rows.
Currently, what we do right now is that we intialize those objects in invoice scene and pass it down to every block, every nested children components and also functions to allow children components can update, therefore, this is really inefficient way(we all know) to achieve our purposes. In order to solve this problem, we will define "app state" for invoice create page and share across scene and nested children.
- In Redux: we will use
async reducertechinue, definereducersactionsselectorsand inject it when a component is mounted.
📁 Folder structure
Invoices
├── CreateOrEditInvoice
│ ├── components
│ ├── state
│ │ └── invoiceCreated.ts <-- our state here
│ └── CreateOrEditInvoice.tsx
🐞 Code
const initialState = {
selectedCustomer: undefined,
invoice: undefined,
numberOfInvoiceRows: 0,
};
const invoiceReducer = (state: State = initialState, action: Action) => {
switch (action.type) {
case SET_CUSTOMER:
...
case UPDATE_INVOICE:
...
case UPDATE_NUMBER_OF_ROWS:
...
}
}
const setCustomerAction = () => {
...
}
const updateInvoice = () => {
...
}
const updateNumberOfRows = () => {
...
}
const mapStateToProps = ({ app }: Store) => {
return app.invoiceCreated;
}
const mapDispatchToProps = (dispatch: Dispatch<Action>) => {
return {
setCustomerAction,
updateInvoice,
updateNumberOfRows,
}
}
const withInvoiceCreated = asyncConnect('invoiceCreated', invoiceReducer, mapStateToProps, mapDispatchToProps);
export default withInvoiceCreated;
Checkout experiment/redux-improvement branch for more details.
- In MobX: Will do it after finishing redux improvement 😂.
All data that we get from our severs will be located inside this state. Our domain state has:
entities: get/post response will be normalized and put into this state.session: current user session.signalR: keep data between our server and websocket.
It is quite similar to what you are doing right now

