I have been working in a small company to develop a mall project with React-Native. Due to lack of guidance and experience, it was very difficult to bury some holes in the early stage, and then decided to carry out a substantial reconstruction of the project. I set a number of goals for myself, but unfortunately before I could achieve them all, the company went bankrupt because of the epidemic. Here to share a part of their own experience, I hope to give some help to the students in the same pit.


Project structure adjustment

Analysis of the

The current project divides the structure (one page and one directory) by the boundary of pages. The problems of this division are as follows:

  1. Dozens of pages crammed into the same directory, adding to the burden of understanding;
  2. Repeatable components across pages are not easy to reuse;
  3. Page-based partitioning distracts programmers and reduces their understanding of the business

The solution

Use business-bounded partitioning (a directory for each feature, with pages, components, and so on related to that feature). Split the page into functional components and place them under the corresponding business module. Then assemble it into a page.

At the same time, Redux should repartition its structure along modular boundaries. Ex. :

- common / / application level of common components | -- - the Header. The ts | - Button. Ts - the features / / deposit business module | - shop / / electricity module | - data / / shop business related data source, Such as remote request | - story / / shop business related redux directory | - common / / the business level of reusable components | | - components / / general components - service / / business logic | - pages. The ts | | - HomePage. Ts - GoodsPage. Ts / / Header and GoodsList assembly into the page

The above directories are not static, and can be increased or decreased according to the complexity of their own projects.


Simplify the story

Analysis of the

Most of the current page state and even remote request data is in Redux, which is unnecessary. The main purpose of Redux is to manage the public state. The private state can be left to the components themselves. Too many states will only lead to a bloated and complex ReDUx structure.

The solution

There are only two states suitable for storing in Redux:

  1. State is shared by two or more components distributed on different pages;
  2. After the component is unmounted and remounted, the state remains on the page.

Other states are maintained by the component itself. For component-level state, use setState. If the page-level state needs to be shared, use a provider.

Also, be careful not to mindlessly put the request backend interface into asynchronous middleware. Only if you are sure that the data returned by the interface is what Redux needs, can you put it in there.


Simplify state management libraries

Analysis of the

The previous project used redux + redux-saga for state management. In fact, our project is small and state management is not complex, so using this combination just adds complexity.

Even for larger projects, there are better libraries to choose from.

The solution

In general, mobx or REdux + redux-thunk will suffice for small to medium sized projects. You can also use the official redux simplification tool: redux-utils. There are also two good third-party management libraries, rematch and Unstated-next.

Rematch Warehouse address unstated- Next Warehouse address

If you want to continue using redux for state management, rematch is recommended (and rematch is compatible with Redux). If you are familiar with react-hooks and are fed up with redux’s verbose code, it is recommended to try unstood-next.

These two libraries are too long to cover in detail, so if you’re still using the original Redux, you’re advised to check them out.


Extract the API layer and clean the API layer

Analysis of the

The code that requests the back-end interface is written directly into the component or redux, resulting in messy logic and not easy to maintain.

Currently, after the data is retrieved from the backend, the objects returned by the backend are used directly for presentation. When the back-end field, data type, or interface changes, the front-end interface needs to be modified. When this interface is used by multiple components, it can cause extensive changes.

The solution

Add an API layer inside the module to encapsulate request-related code with functions.

Add a data cleaning layer. The background return data is passed into the function, converted into a fixed format object, and then passed to the view layer for use. In this way, when the back-end interface changes, only the cleaning layer needs to be changed.


Out of the service layer

Analysis of the

In the current project, the presentation of the page (the view layer) is put together with the business logic, resulting in a bloated view layer. In addition, business logic is scattered in components, resulting in low reusability of business logic and difficult to maintain.

The solution

The service layer is placed in the module, and the required services in the module are planned and realized through interfaces in advance (the logic contained in the Service layer must correspond to the business logic in the module). Components can be imported as needed.


Improve the routing structure and dynamically add routes through configuration files

Analysis of the

Currently, the configuration of page routes is flat, and all pages are on the top layer of routes. As a result, the route configuration file is bloated.

The maintainer must be familiar with the route name corresponding to the page to jump to the page, which increases the understanding burden of the project.

After the route name corresponding to the page is changed, the redirected places on the page must also be changed, which increases the maintenance cost.

The solution

In each module, the routing path is defined through the configuration file (the path format is module name/page name, for example: Shop/GoodsPage). The routing file reads and traverses the configuration file, and dynamically adds the routing path.

Maintain an additional constant file representing the route name. Import constants from this file to solve the problem caused by route name modification. (If no route name modification is required, the problem can be ignored.)


Writing unit tests

Analysis of the

The best way to maintain code quality is to continue refactoring, which can lead to faulty project functionality. By writing unit tests, you can ensure that functionality remains the same after each change.

However, the project did not introduce unit testing early, resulting in too many places to test and too much work.

The solution

Do not write large-scale unit tests for the time being. When you modify a feature later, you can add the corresponding unit tests.

For new functionality, unit tests must be written.


Perfect type checking

Analysis of the

Dynamic js types not only cause hidden problems, but also increase the workload of refactoring. With typescript, you can avoid most low-level errors caused by typing problems.

However, with typescript, there is a lot of work to do to validate types and write interfaces.

The solution

Reduce the workload by introducing typescript with looser type checking policies, such as allowing implicit any types.

When a feature can be modified, the type can be fixed.

Wait until there are few type errors left, and then fix the remaining errors.

Then add the type checking command to the packaging directive, ensuring that type checking is performed every time a package is packaged.

Finally, a strict type-checking strategy can be phased in.


One caution about refactoring:

Set goals and estimate the effort before a major refactoring, preferably no more than one day, and break them down into smaller goals.

Continuous refactoring is a better approach than large-scale refactoring. Reflect on the past code in your daily work, such as optimizing variable names, decoupling functions, and splitting components. Otherwise the problems will pile up.

Save the code before refactoring. When refactoring, don’t change a large chunk of code at once and then check for errors. Remember to make as few changes as possible.

Typescript and unit tests make refactoring much easier.


Appendix: Why-what-when – How about refactoring

— From Geek Time, The Beauty of Design Patterns, section 27 by Wang Zheng

1. Purpose of refactoring: Why?

In the case of a project, refactoring keeps code quality under control and not beyond redemption. On an individual level, refactoring exercises one’s coding skills and is a very rewarding experience. It is the training ground where we learn classical design ideas, principles, patterns, programming specifications and other theoretical knowledge.

2. Refactoring objects: Refactoring what?

According to the scale of reconstruction, we can roughly divide the reconstruction into large-scale high-level reconstruction and small-scale low-level reconstruction.

Large-scale high-level refactoring includes layering, modularizing, decoupling, teasing out the interactions between classes, abstracting and reusing components, and so on. This part of the work is more abstract, more top-level design ideas, principles, patterns.

Small-scale low-level refactorings include normal naming, annotation, correction of excessive function parameters, elimination of oversized classes, extraction of duplicate code and other programming details, mainly for class and function level refactorings. Small-scale low-level refactoring relies more on theoretical knowledge of coding specifications.

3. When to refactor: When?

Establish a sense of continuous refactoring and make refactoring an essential part of your daily development, rather than waiting until the code has a major problem and then refactoring it.

4. Refactoring methods: How?

Large-scale, high-level refactoring is difficult and requires organization, planning, and step-by-step sprinting to keep the code in a working state. Small-scale low-level refactorings can be done anywhere if you want and have the time because of their small scope of impact and short change time.