I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.
I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.
Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?
We used the add() method to add a class on the body object. We can access the body element on the document object. If the class is already present on the body element, it won't get added twice. You can pass as many classes to the add() method as necessary.
If you want to add a class ( className ) to the body tag of a a NextJS page, you'll need to add it via JavaScript in the useEffect (or componentDidMount if you're using class-based components) Lifecycle function. This adds TailWindCSS's font-light and text-gray-700 classes to the body tag.
If you need to add a className there, it's clear that either you add a dom element in this case another div or add the className to its parent. Show activity on this post. Using Fragment means not adding an extra node to DOM. If you want to assign a className to a node then you'll have to use div .
TL;DR use document.body.classList.add
and document.body.classList.remove
I would have two functions that toggle a piece of state to show/hide the modal within your outer component.
Inside these functions I would use the document.body.classList.add
and document.body.classList.remove
methods to manipulate the body class dependant on the modal's state like below:
openModal = (event) => {
document.body.classList.add('modal-open');
this.setState({ showModal: true });
}
hideModal = (event) => {
document.body.classList.remove('modal-open');
this.setState({ showModal: false });
}
With the new React (16.8) this can be solved with hooks:
import {useEffect} from 'react';
const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);
export default function useBodyClass(className) {
useEffect(
() => {
// Set up
className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);
// Clean up
return () => {
className instanceof Array
? className.map(removeBodyClass)
: removeBodyClass(className);
};
},
[className]
);
}
then, in the component
export const Sidebar = ({position = 'left', children}) => {
useBodyClass(`page--sidebar-${position}`);
return (
<aside className="...">
{children}
</aside>
);
};
Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>
Like what @brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)
In this top-level component:
modalOpen
) to toggle the CSS classhandleOpenModal
& handleCloseModal
) to modify the boolean state.<Modal />
componentIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With