Blog>
Snippets

Implementing Code Splitting with Redux in a React Application

Provides an example of how to implement dynamic imports using React.lazy and React.Suspense in combination with Redux to achieve effective code splitting.
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import React, { Suspense, lazy } from 'react';
import ReactDOM from 'react-dom';

const rootReducer = combineReducers({
  //... your other reducers here
  // you have to import them above
});

const store = createStore(rootReducer);

// Lazy-load a component and its associated reducer
const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Provider store={store}>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </Provider>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
This code initializes the Redux store with a root reducer and sets up the React application with a Provider from react-redux. Within the App component, React.lazy is used to dynamically import 'LazyComponent' which is rendered inside a Suspense component to allow for code splitting and displaying a fallback loading state until the component is ready.
// LazyComponent.js
import React from 'react';
import reducer from './lazyReducer';
import { useDispatch } from 'react-redux';

// Inject the lazy-loaded reducer
const enhanceReducer = async (store, newReducer) => {
  store.asyncReducers.lazy = newReducer;
  store.replaceReducer(combineReducers(store.asyncReducers));
};

export default function LazyComponent({ store }) {
  const dispatch = useDispatch();
  // Assumes reducer is loaded at the same time as component
  React.useEffect(() => {
    enhanceReducer(store, reducer);
  }, [store]);

  // Component logic
  return <div>Lazy Component Content</div>;
}
The LazyComponent imports its own reducer and uses the Redux useDispatch hook. On mount, it utilizes an enhanceReducer function to inject the lazy-loaded reducer into the existing store. It updates the store with the lazy-loaded reducer by combining it with existing reducers, which allows for the use of code splitting for reducers as well.
// lazyReducer.js
// Define the reducer for the lazy-loaded component
export default function lazyReducer(state = {}, action) {
  switch (action.type) {
    case 'LAZY_ACTION':
      return { ...state, ...action.payload };
    default:
      return state;
  }
}
This is the reducer for the lazy-loaded component. It could contain any number of case statements related to the actions dispatched by the LazyComponent, which would modify the state as necessary.