State is data for the entire application and is essentially a common object. State determines how the entire application’s components will be rendered and what the results will be. You can say that State is the soul of the application and components are the body of the application.

Therefore, in the early stage of project development, it is particularly important to design a robust and flexible State, which is of great help to the subsequent development. Note that it is not mandatory that all data be stored in State, and that some component data can be left to the component itself.

In the process of designing the State, it is necessary to split and transform the State. Generally speaking, we can split and transform the State from two dimensions: horizontal and vertical.

How to split State horizontally?

In general, the larger the application, the more data the State contains and the more complex the data structure. To facilitate the management of this complex data structure, we usually do a horizontal split by data category. Take CuriosityDaily as an example. We have a list of home pages, a list of research institutes:

{
    homeList: [{}, {}],
    paperList: [{}, {}]
}Copy the code

Next, consider this: Curiodaily also has other types of lists, such as tag lists and category lists:

{homeList: [{}, {}], paperList: [{}, {}] / / other list page data tagList: [{}, {}], categoryList: [{}, {}}Copy the code

So should our data structures be designed like this? In this case, our State will soon become bloated and difficult to maintain, and the next Reducer design will become extremely difficult. Then we try to make a horizontal split design according to the data category:

Articles: {// list: [{}, {}], Tag: [{}, {}]}, // Papers: List: [{}, {}]}}Copy the code

Yes, essentially archive the same kind of data: Strategies articles: state.articles, save data related to your essay, like various list pages Papers: Strategies. Papers, save data related to your study, like various list pages

So, what are the benefits of this design? Strategy for Partition makes State data decoupled and independent from each other, we can manage articles and papers separately. Strategies data can be differentiated by categories, and the Reducer processing logic of the same data can be put together, which is easier to maintain in the code.

One might ask, how do you manage articles and papers separately? Consult another blog Reducer best practices

Splitting State is dial-and-conquer: Articles data has a manager and Papers data has a manager to keep the code logical and separate. With data split, we can manage data in finer granularity. This is horizontal split. How can we solve vertical problems? The so-called vertical, in fact, is the problem of deep data nesting.

How to transform State vertically?

Why solve the problem of data nesting?

Pick up two chestnuts and you’ll see. Strategies for Strategies Scenario 1: Strategies for Strategies Daily has many list pages, each list stores a detailed field for an article/paper, is grossly redundant and there is no way to synchronize updates.

List: [{id: 1, title: XXX}, {id: 3, title: XXX}, {id: 3, title: XXX} Tag: [{id: 2, title: XXX}, {id: 3, title: XXX}]}}Copy the code

Scenario 2: Strategies for Reading Scenario 2: Strategies for Reading the questionnaire for Curiosity Daily had a complicated structure that led to three cycles of adding, deleting, changing, and checking — a nightmare.

// The paper table contains information about paper and an array of questions. // The question table contains information about question and an array of options. // The option table contains information about options. papers: [{ id: xxx, title: xxx, questions: [{ id: xxx, title: xxx, sequence: xxx, options: [{ id: xxx, title: xxx, sequence: xxx }] }] }]Copy the code

Now what if we select an option and need to update option’s selected=true? We need to loop through three layers, find option, and set Selected = True. This is a very simple requirement. There are many similar scenarios where we need to traverse the data structure vertically, which is not only poor performance, redundant code, and difficult to maintain.

So the idea is that data flattening, what do WE mean by data flattening? Take a look at the following code to make it clear:

Papers: [id1, id2] papersHash: {id1: {id: XXX, title: XXX, questions: [id1, id2]}} questionsHash: { id1: { id: xxx, title: xxx, sequence: xxx, options: [id1, id2] } } optionsHash: { id1: { id: xxx, title: xxx, sequence: xxx } }Copy the code

As you can see from the above structure, after flattening, the data is extracted into a hash object, and the value of the specified object can be easily obtained based on the key-value pair. The associated part stores only ids as indexes. For example, in the previous scenario, if we wanted to update an option, we could simply update the value in optionsHash based on the ID.

So how do you simply flatten the data?

Normalizr.js is a library that is easy to use. Define a schema, declare the relationships between schemas, and then you’re done. Just to give you an example of schema, for more information, you can go to the official documentation.

import { Schema, arrayOf, normalize } from 'normalizr'; const paperSchema = new Schema('papers'); const questionSchema = new Schema('questions'); const optionsSchema = new Schema('options'); // declare paper.quesitons to be an arrayOf questionschema. define({questions: arrayOf(questionSchema)}); Questionschema. defind({options: arrayOf(optionsSchema)}); // declare question.options, an arrayOf optionsSchema. Papers let normalizeData = normalize(papers, arrayOf(paperSchema)); NormalizeData = {// result: [id1, id2...] , // entities: { // papers: { // id1: {} // }, // questions: { // id1: {} // }, // options: { // id1: {} // } // } // }Copy the code

At this point, both the horizontal (divide-and-conquer) and vertical (flattening) splits of State have been completed. The lesson of Redux is data-oriented programming. Therefore, it is the soul of a project to design the data structure of State according to the actual requirements before development. It also influences and determines subsequent development. Of course, if you need to adjust the State in the early stage of development, please adjust it boldly. As the product is approaching maturity, the adjustment of State should be as cautious as possible.

What’s the summary?

Remember, the need to comb through the requirements before development and then design the data structure of State is as important as database design is to the background. It influences and determines subsequent development. State itself is a common object. For finer grained management and maintenance, we consider splitting and transforming State from horizontal/vertical dimensions.

Horizontal Design State: It is unwise to dump State into a super-reducer, we should split State according to data categories and provide specialized managers for different data categories. Design State vertically: Make State as flat as possible so that data can be increased, deleted, changed and checked more flexibly and the problem of redundant data can be avoided.

In addition, State is an ordinary object in nature. The reason why State is designed so deliberately is to make development speed faster, logic clearer and maintenance more convenient. If your application is inherently simple, don’t overdesign State, or even Redux.