I wanted to allow users to change the theme of the application by picking which theme they want the body's background color changes and all button colors. But the problem is that whenever I use document.querySelectorAll('.btn-theme-1').style.backgroundColor it tells me that it cannot read those properties.
I know of useRef() but in my case I am trying to select all buttons throughout the entire application. Not just one element in the current component. So I would like to know if there is a way to fix what I am attempting or if I am doing this the wrong way.
Here is the code for what I tried. This is my pick theme component:
import ColorThemes from '../data/ColorThemes';
import { useEffect } from 'react';
const PickTheme = () => {
const changeTheme = (c) => {
document.body.style.background = c.default || c.bgColor;
document.body.style.color = c.bodyColor;
document.querySelector('.bi-quote').style.color = c.buttonBg;
document.querySelectorAll('.text-color').forEach(el => el.style.color = c.fontColor)
document.querySelectorAll('.btn-theme-1').forEach(el => {
el.style.color = c.buttonColor;
el.style.backgroundColor = c.buttonBg;
});
};
useEffect(() => {
},[changeTheme]);
return(
ColorThemes.background.map(c => {
if(c.bgColor) {
return(
<button type="button" key={c.bgColor} className="btn btn-light me-2 p-3 rounded-5" onClick={() => changeTheme(c)} style={{backgroundColor: c.bgColor}}></button>
);
} else {
return(
<><br/><button type="button" key={c.default} className="btn btn-light me-2 mt-2 rounded-5" onClick={() => changeTheme(c)}>Default</button></>
);
}
})
);
};
export default PickTheme;
It successfully changes the bodys color and background color but not the other classes. I tried with and without useEffect and still receive the same issue.
If I comment out everything except the last selector, the buttons then change colors. So maybe it is conflicting or cannot change everything at once, for example:
const changeTheme = (c) => {
// document.body.style.background = c.default || c.bgColor;
// document.body.style.color = c.bodyColor;
// document.querySelector('.bi-quote').style.color = c.buttonBg;
// document.querySelectorAll('.text-color').forEach(el => el.style.color = c.fontColor)
document.querySelectorAll('.btn-theme-1').forEach(el => {
el.style.color = c.buttonColor;
el.style.backgroundColor = c.buttonBg;
});
};
This changes the buttons background and color after commenting out the other parts.
CodePudding user response:
I know of useRef() but in my case I am trying to select all buttons throughout the entire application. Not just one element in the current component.
Using .querySelector
or any other selector will select only those elements that are currently rendered, actually. So if you e.g. toggle the state and component re-renders with different elements, they will not be affected with your change, which will result in partially toggled theme for different elements.
You should either set a context, wrapping whole App or set a redux variable holding info which theme is currently selected. Then, you will be able to manipulate styles using e.g. theme
in styled components: https://styled-components.com/docs/advanced#theming or just toggling classNames with css modules, basing on that variable.
CodePudding user response:
You can use useRef()
with a function that runs on each element and add them to it, let me dive deeper into it.
Let's first create a reference containing, for now, an empty array:
const myRef = useRef([])
Great, we now want to populate that. It's going to be in two parts, first, let's make a function that will populates that array:
const addToMyRef = (element) => {
if (element && !myRef.current.includes(element)) {
myRef.current.push(element);
}
};`
Great, we now have a function that takes an element of the DOM as an argument, verifies that it exists and that it is not yet in our array, then adds it.
But now, when will it get triggered? Simply in the ref=
attribute!
<button ref={addToMyRef}></button>
To watch the array, let's just set a listener on it with useEffect()
:
useEffect(() => {
console.log(myRef);
}, [myRef]);
You'll now see that your reference is now a set of them, so you can create a reference per element, or maybe modify the code a little to makes it takes objects to have a all-in-one reference for each element of the dom. (We could imagine it being myRef.buttons/myRef.inputs...)