This guest article on Applozic Blog was written by Nikhil Kumaran S. Nikhil is a web developer with expertise in Frontend technologies. He shares his knowledge by writing blog posts, giving tech talks at conferences and mentoring people on web development. You can find Nikhil on his website, LinkedIn, GitHub and Twitter.
In this blog post, I’ll write about things that we should finalise before starting a react project. These include topics like Rendering, Language selection, Routing, Data fetching, State management, Testing and Folder structure. I will go over each of these topics and provide best practices that we can follow so that our project is scalable and maintainable in the long run.
Let’s start with rendering. Rendering is a process that is triggered by a change of state in some component of your application, when a state change occurs. We have three types of rendering:
- Client side rendering,
- Server side rendering, and
- Pre rendering.
You can choose between these different types of rendering based on your application needs.
If you use
create-react-app to bootstrap your react project, you will get only client side rendering out of the box. For server side rendering and pre rendering, I would recommend using Next.js. Next.js supports all the three renderings.
Here are a few advantages of TypeScript
- Fail fast – Catch errors in compile time.
- Better intellisense.
- Better code readability.
- Fast and confident refactoring.
create-react-app and Next.js have the option to bootstrap your application using TypeScript. TypeScript does have a little learning curve, but I would still highly recommend going with TypeScript.
If you are using Next.js, then routing is provided out of the box. When you create react components inside the pages folder, routes are generated based on the file name of your react components.
If you are using
create-react-app, you have two major options: React router and Reach router. In the future these two will be unified to become React router. For new projects it is recommended to use Reach router as it’s API looks slightly more like the new API so it’ll be easier for you to migrate once the unification happens.
Data fetching can get very messy in the long run, if you just use useEffect for HTTP requests. That’s where
react-query comes in. It provides a powerful and easy to use hook based API for all your data fetching needs. I highly recommend using
These are some of the features of
- Status states for requests.
- Dependent and parallel requests.
- Caching and re-fetching.
- Optimistic updates.
- Dedicated dev tools.
You can check out a tutorial on React Query on Applozic Blog here!
If you are using
react-query, then all your data fetching states like loading states, error states, response states are already handled.
For local component level states, go with the
useState hook. If you have a complex state logic that involves multiple sub-values or when the next state depends on the previous one, go with the
If you need states that need to be shared with multiple components go with
Context. Don’t have a global context unless it’s absolutely necessary, for example, global theme state. In almost all cases, keep states as close to where it’s needed as possible.
If you are dealing with more complex UI states that transition to different states, then you might want to consider giving
XState a try.
To test the functionality of react components in isolation, render that particular component using
testing-library/react and test various functionalities by triggering events and asserting the changes.
To test the presentation components, go with jest snapshots.
To test the custom hooks, create an example component which uses your custom hook, render the example component with react testing library and test the hooks functionality. If the hook is complex, then you might end up creating a bunch of example components which are hard to maintain. For this case, you can use the
renderHook function from
testing-library/react-hooks and test it’s functionality.
For integration tests, render your top level App component using
testing-library/react and test the various functionalities by triggering events and asserting the changes. You can mock the HTTP requests using msw.
End to end testing
End to end tests can give you a great deal of confidence as your tests will run in an actual browser, and mimics the actions that the end user might perform while using your application. For this I would recommend using Cypress or Puppeteer.
If you want to test styles in your application, consider using Cypress Visual Regression or Percy.
Take some time and finalise the folder structure of your project based on the need and follow it. Knowing where a certain functionality resides just by looking at the folder structure will definitely help you in the long run. Here is a folder structure that works for me.
- src/ --- components/ ------ Component.tsx ------ tests/ (components unit tests) --- pages/ --- context/ --- hooks/ ------ useSomeHook.ts ------ tests/ (hooks unit tests) --- utils/ ------ someUtil.ts ------ tests/ (Utils unit tests) --- tests/ (Integration tests and end to end tests) --- assets/ --- public/ --- styles/ --- App.tsx - root level config files for eslint and prettier etc
Do consider these topics before starting your React projects. You don’t have to follow all my recommendations that I provided here. You can use these as a reference and decide based on the application needs and what works for you. Hope you find this useful.
If you have any questions or suggestions, feel free to leave a comment below.
- Different rendering Next.js provides – https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation
- React router and reach router unification – https://reacttraining.com/blog/reach-react-router-future
- React query – https://react-query.tanstack.com
- msw for mocking HTTP requests – https://mswjs.io
- Cypress – https://www.cypress.io