Creating a complex React root: order and dependencies

by Dan Edwards, 24 June 2024

Creating a complex React root: order and dependencies

When creating a complex React root, it's crucial to understand the dependencies and order of the various providers and libraries being used. This articleData will walk you through setting up a comprehensive React application using multiple providers, ensuring that your application is both robust and maintainable.

Code Overview

Let's look at the oddly shaped code for creating a complex React root:

index.js
JavaScript
1import React from 'react';
2import ReactDOM from 'react-dom/client';
3import { Provider } from 'react-redux';
4import { PersistGate } from 'redux-persist/integration/react';
5import { RouterProvider } from 'react-router-dom';
6import { HelmetProvider } from 'react-helmet-async';
7import { ThemeProvider } from 'styled-components';
8import { IntlProvider } from 'react-intl';
9import { ApolloProvider } from '@apollo/client';
10import { client } from './apolloClient';
11import { store, persistor } from './store';
12import { router } from './router';
13import { theme } from './theme';
14import { messages } from './messages';
15import App from './App';
16
17const render = () => {
18  const root = ReactDOM.createRoot(document.getElementById('root'));
19
20  root.render(
21    <React.StrictMode>
22      <HelmetProvider>
23        <ApolloProvider client={client}>
24          <IntlProvider locale="en" messages={messages}>
25            <ThemeProvider theme={theme}>
26              <Provider store={store}>
27                <PersistGate loading={null} persistor={persistor}>
28                  <RouterProvider router={router}>
29                    <App />
30                  </RouterProvider>
31                </PersistGate>
32              </Provider>
33            </ThemeProvider>
34          </IntlProvider>
35        </ApolloProvider>
36      </HelmetProvider>
37    </React.StrictMode>
38  );
39};
40
41render();

Order and Dependencies

React.StrictMode

  • Purpose: It helps to highlight potential problems in an application, such as errors, deprecated APIs, side effects, and unused variables.
  • Order Explanation: This is wrapped around the entire application to ensure best practices are followed throughout the development phase.

HelmetProvider

  • Purpose: Manages changes to the document head, like title and meta tags. Remember to use react-helmet-async, as react-helmet is depreciated.
  • Order Explanation: Placed at a high level to ensure any changes to the document head are managed before rendering other components.

ApolloProvider

  • Purpose: Integrates Apollo Client for GraphQL data management.
  • Order Explanation: Placed early to ensure GraphQL data is available to child components that might need it.

IntlProvider

  • Purpose: Provides internationalization support, allowing the app to handle multiple languages.
  • Order Explanation: Ensures that internationalization is configured before any themed or Redux-managed content is rendered, as some components may rely on translated strings.

ThemeProvider

  • Purpose: Provides theming capabilities, such as dark/light mode.
  • Order Explanation: Ensures that theming is applied before any styled-components are rendered.

Provider (Redux)

  • Purpose: Integrates Redux for state management.
  • Order Explanation: Configured before PersistGate to ensure the Redux store is available for state management throughout the app.

PersistGate

  • Purpose: Delays the rendering of the app's UI until the persisted state has been retrieved and saved to Redux.
  • Order Explanation: Ensures the app's UI is rendered only after the persisted state has been rehydrated.

RouterProvider

  • Purpose: Integrates routing capabilities using React Router.
  • Order Explanation: Configured last among the providers to ensure all routing logic is applied, allowing the app to manage navigation correctly.

App Component

  • Purpose: The root component of the application where all other components are nested.
  • Order Explanation: Finally, the App component is rendered, leveraging all the configured providers to ensure a cohesive and well-managed application state.

By following this order, each provider is correctly set up with its necessary dependencies, ensuring that the application runs properly.