DEV Community

Cover image for Global Modal in react using redux toolkit
Arshan Nawaz
Arshan Nawaz

Posted on • Edited on

5 1 1 1 1

Global Modal in react using redux toolkit

To build a global modal using Redux Toolkit where you can dynamically render different components based on a componentName passed to the modal, we'll break the process down into the following steps:

1. Create the Components Index File: This file will import all the components and export them as an object.
2. Set Up Redux Slice: This slice will handle the state of the modal, including whether it's open and which component should be rendered.
3. Create the Modal Component: This will be the global modal component that checks which component should be rendered based on the state.
4. Dispatching Modal Actions: You'll call these actions to open the modal and specify which component to render.

Step 1: Components Index File (components/Index.jsx)
This file will import all your components and export them as an object.

// components/Index.jsx
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
// Import all other components

const components = {
  ComponentA,
  ComponentB,
  // Add all other components
};

export default components;

Enter fullscreen mode Exit fullscreen mode

Step 2: Redux Slice for Modal (features/modal/modalSlice.js)
This slice will handle opening and closing the modal, as well as tracking which component to render.

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  isOpen: false,
  componentName: null,
  componentProps: {},
};

const modalSlice = createSlice({
  name: 'modal',
  initialState,
  reducers: {
    openModal: (state, action) => {
      state.isOpen = true;
      state.componentName = action.payload.componentName;
      state.componentProps = action.payload.componentProps || {};
    },
    closeModal: (state) => {
      state.isOpen = false;
      state.componentName = null;
      state.componentProps = {};
    },
  },
});

export const { openModal, closeModal } = modalSlice.actions;

export default modalSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

Step 3: Global Modal Component (components/GlobalModal.jsx)
This component will check the current state and render the appropriate component from the components index.

// src/components/Modal/GlobalModal.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { FaTimes } from 'react-icons/fa'; // Importing the cross icon from react-icons
import components from '../Index';
import { closeModal } from '../../redux/features/modal/modal.slice';

const GlobalModal = () => {
  const dispatch = useDispatch();
  const modal = useSelector(state => state.modal);
  const { isOpen, componentName, componentProps } = modal;

  if (!isOpen || !componentName) return null;

  const ComponentToRender = components[componentName];

  return (
    <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
      <div className="bg-white p-6 rounded-lg relative shadow-lg">
        <button
          className="absolute top-2 right-2 text-gray-400 hover:text-gray-600"
          onClick={() => dispatch(closeModal())}
        >
          <FaTimes size={24} />
        </button>
        <ComponentToRender {...componentProps} />
      </div>
    </div>
  );
};

export default GlobalModal;

Enter fullscreen mode Exit fullscreen mode

Step 4: Using the Modal in Your App
To open the modal and render a specific component, you would dispatch the openModal action from anywhere in your app.

import { useDispatch } from 'react-redux';
import { openModal } from './features/modal/modalSlice';

const SomeComponent = () => {
  const dispatch = useDispatch();

  const handleOpenModal = () => {
    dispatch(openModal({ componentName: 'ComponentA', componentProps: { someProp: 'value' } }));
  };

  return (
    <div>
      <button onClick={handleOpenModal}>Open Modal</button>
    </div>
  );
};

export default SomeComponent;
Enter fullscreen mode Exit fullscreen mode

Integration
Finally, ensure your GlobalModal component is included in your app's root component so that it's always available.

import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import GlobalModal from './components/Modal/GlobalModal';
import SomeComponent from './SomeComponent';

const App = () => (
  <Provider store={store}>
    <SomeComponent />
    <GlobalModal />
  </Provider>
);

export default App;
Enter fullscreen mode Exit fullscreen mode

To build a global modal using Redux Toolkit where you can dynamically render different components based on a componentName passed to the modal, we'll break the process down into the following steps:

2nd method => implementation with the onSave functionality and React.memo for optimizing re-renders.

Step 1: Components Index File (components/Index.jsx)
This file will import all your components and export them as an object.

// components/Index.jsx
import React from 'react';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
// Import all other components

const components = {
  ComponentA: React.memo(ComponentA),
  ComponentB: React.memo(ComponentB),
  // Add all other components and memoize them
};

export default components;


Enter fullscreen mode Exit fullscreen mode

Step 2: Redux Slice for Modal (features/modal/modalSlice.js)
This slice will handle opening and closing the modal and onSave, as well as tracking which component to render.
import { createSlice } from '@reduxjs/toolkit';

// features/modal/modalSlice.js
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  isOpen: false,
  componentName: null,
  componentProps: {},
  onSave: null, // Add onSave to the state
};

const modalSlice = createSlice({
  name: 'modal',
  initialState,
  reducers: {
    openModal: (state, action) => {
      if (!state.isOpen) {
        state.isOpen = true;
        state.componentName = action.payload.componentName;
        state.componentProps = action.payload.componentProps || {};
        state.onSave = action.payload.onSave || null;
      }
    },
    closeModal: (state) => {
      if (state.isOpen) {
        state.isOpen = false;
        state.componentName = null;
        state.componentProps = {};
        state.onSave = null; // Reset onSave when modal is closed
      }
    },
  },
});

export const { openModal, closeModal } = modalSlice.actions;

export default modalSlice.reducer;

Enter fullscreen mode Exit fullscreen mode

Step 3: Global Modal Component (components/GlobalModal.jsx)
This component will check the current state ,onSave and render the appropriate component from the components index.

import React, { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { closeModal } from '../features/modal/modalSlice';
import components from './Index';

// Selector to get the modal state
const selectModalState = (state) => ({
  isOpen: state.modal.isOpen,
  componentName: state.modal.componentName,
  componentProps: state.modal.componentProps,
  onSave: state.modal.onSave,
});

const GlobalModal = () => {
  const dispatch = useDispatch();
  const { isOpen, componentName, componentProps, onSave } = useSelector(selectModalState);

  if (!isOpen || !componentName) return null;

  const ComponentToRender = components[componentName];

  const handleSave = useCallback(() => {
    if (onSave) {
      onSave(componentProps); // Pass componentProps to the onSave callback
    }
    dispatch(closeModal()); // Optionally close the modal after saving
  }, [onSave, componentProps, dispatch]);

  return (
    <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
      <div className="bg-white p-4 rounded shadow-lg">
        <ComponentToRender {...componentProps} />
        <div className="mt-4 flex justify-end space-x-2">
          <button className="text-blue-500" onClick={handleSave}>
            Save
          </button>
          <button className="text-red-500" onClick={() => dispatch(closeModal())}>
            Close
          </button>
        </div>
      </div>
    </div>
  );
};

export default React.memo(GlobalModal);

Enter fullscreen mode Exit fullscreen mode

Step 4: Opening the Modal with the Save Action
When you want to open the modal and use the onSave function, you do so as follows:

import React from 'react';
import { useDispatch } from 'react-redux';
import { openModal } from './features/modal/modalSlice';

const SomeComponent = () => {
  const dispatch = useDispatch();

  const handleOpenModal = () => {
    const onSave = (props) => {
      console.log('Saved with props:', props);
      // Perform your save logic here
    };

    dispatch(openModal({
      componentName: 'ComponentA',
      componentProps: { someProp: 'value' },
      onSave: onSave,
    }));
  };

  return (
    <div>
      <button onClick={handleOpenModal}>Open Modal</button>
    </div>
  );
};

export default React.memo(SomeComponent);

Enter fullscreen mode Exit fullscreen mode

Integration
Finally, ensure your GlobalModal component is included in your app's root component so that it's always available.

import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import GlobalModal from './components/GlobalModal';
import SomeComponent from './SomeComponent';

const App = () => (
  <Provider store={store}>
    <SomeComponent />
    <GlobalModal />
  </Provider>
);

export default App;
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Components Index: This file maps all your components to their names, so you can render them dynamically.
  • Modal Slice: The slice manages the state of the modal, including which component to render and its props.
  • Global Modal: This component renders the appropriate component inside a modal based on the Redux state.
  • Usage: You can open the modal by dispatching the openModal action with the desired component name and props.

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (1)

Collapse
 
talha_gamingyt_1998dfdac profile image
TALHA Gaming yt

Amazing explanation brother

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay