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;
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;
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;
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;
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;
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;
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;
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);
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);
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;
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.
Top comments (1)
Amazing explanation brother