I've got a dark mode switch that I want to style according to what theme the user has stored in localstorage. It works fine until refresh. Somehow I think my theme context is undefined so my dm const always favours the second value in the ternary.
import styles from "./ThemeSwitchToggle.module.scss"
const { theme, setTheme } = useContext(FormatContext)
// Context updated in another component based on localstorage
const dm = theme === "light" ? styles.Light : styles.Dark
// Always favours second parameter
<div className={`${styles.themeSwitch} ${dm}`}
<div
className={`${styles.themeSwitchDot} ${dm}`}
</div>
</div>
Toggle style in dark mode:
Toggle style in light mode:
After refresh if in light mode when refreshing (somehow uses styles.Dark even though theme = "light")
styles.Dark favoured
Refreshing in dark mode applies the correct styles since it always favours styles.Dark. If i switch places on the ternary, the opposite problem occurs.
E.g:
const dm = theme === "light" ? styles.Dark : styles.Light
Result, after refresh in dark mode:
styles.Light favoured
It also throws the warning:
Warning: Prop className did not match.
Server: "ThemeSwitchToggle_themeSwitchDot__uot4R
ThemeSwitchToggle_Light__nL8dx"
Client: "ThemeSwitchToggle_themeSwitchDot__uot4R
ThemeSwitchToggle_Dark__JtIx9"
I think I understand the issue, that next essentially prerenders the page before the theme context (FormatContext) is read/initialized, and then client-side rendering takes over and updates the component correctly (but somehow does not apply the right style), but this part of next is new to me.
How does next actually handle renders when the style is applied like
this?
Does this have any other implication aside from just styles? Is
the serverside content rendered before contexts are initialized every
time?
Side note: the actual functionality is entirely based on animations, which I've not included here; but essentially, this is only to apply the style initially. When clicked the animations take over and overwrite the styles.
ThemeSwitchToggle.module.scss
Related
So a little disclaimer: I am completely and utterly self taught. Bear over with me if I'm being a clown.
Anyways, I am currently working on a some platform and in need for a dropdown functionality. That's simple right? Just use HTML5 select tag. However option tags can't be styled :>
So onwards to build my own. The HTML5 select tag uses keyboard input (up/down/enter) for those with disabilities, and I thought I would implement that too. That did present a problem though: The :hover selector collided with my custom attribute, which I use to style keyboard selected items (&[data-selected=true] to be precise).
So onwards to implement my own :hover. And this is where my bewilderment starts.
const handleChildMouseOver = () => {
const items = Array.from(listItem.current?.children!); // The wonders of typescript XD
for (const item of items) {
if (item === event.target) {
item.setAttribute("data-selected", "true");
} else {
item.removeAttribute("data-selected"); // I'm removing the attribute, rather than toggling it, because I got components with 3 states: On, off, and default.
}
}
}
(...)
<ul css={css.list} /*emotion prop*/ data-toggled={toggled} /*parent state*/ onMouseOver={handleChildMouseOver}>
{children} // parent prop
</ul>
So it works as intended, which is fine. But I recall from my pre-react days that you should never manipulate the DOM in loops, as it causes repaints on every iteration. However when I look at the Dev Tools performance profiler, I barely see any "Paints", 8 or so, even when I'm switching hover targets like a madman. What I do see is one million "Composite layer". Oh, and as a bonus React doesn't re-render. Which is fine right? 'Cause I'm not really changing the state of anything, just adding some CSS.
So my question boils down to: Am I being bonkers or smart?
N.B.: I would love to share the actual component, but seeing as this is my first post on stackoverflow, I've got no clue how you do those fancy script tag. Well github is involved somehow, I know that much 🤔
I have been creating a set of reusable components that I had been styling using classes prop to override the MUI classnames. I then extracted a lot of the common styling into a theme to avoid repetition in the more complex components. The theme is wrapping each of the components using the withTheme HOC.
I am now realising that there are places we need to override the style for one-off cases. I thought I should be able to do this using the withStyles HOC but it doesn't seem to work for me.
Codepen at https://codesandbox.io/s/overriding-a-withtheme-with-withstyle-hoc-0m9cm
MyReusableThemedComponent - is the reusable component (that is really just wrapping Material UI tabs with a theme)
CustomOverideTabs - is my implementation of the MyReusableThemedComponent where I am trying to override the Material-UI textTransform by making the text lower case.
const StyledTabs = withStyles({ root: { textTransform: "lowercase" } })(
MyReusableThemedComponent
);
I believe the transform: uppercase is the default for the MuiTab-root class, but even specifying it in the theme doesn't seem to make a difference.
TIA
The effect of withStyles is to inject a classes prop into the wrapped component (MyReusableThemedComponent in your case), but you aren't doing anything with that injected prop except passing the entire props object to useStyles during the creation of tabsStyle. This will merge the two sets of classes, but then you would need to leverage tabsStyle.root somewhere to have any effect.
You have the following code for rendering the Tab elements:
<Tab
key={index}
label={tab.tabTitle ? tab.tabTitle.toString() : "tab" + { index }}
disabled={tab.disabled}
classes={{
root: tabsStyle.tabRoot,
selected: tabsStyle.selectedTab
}}
/>
This is leveraging tabsStyle.tabRoot as the root class, but tabRoot hasn't been defined anywhere. The textTransform works as intended if you change this to root: tabsStyle.root, or if you leave the Tab rendering unchanged, you can get it to work by changing the rule name in your withStyles call to be tabRoot (e.g. withStyles({ tabRoot: { textTransform: "lowercase" } })).
Example using tabsStyle.tabRoot (i.e. only changing the withStyles argument): https://codesandbox.io/s/overriding-a-withtheme-with-withstyle-hoc-fxybe
Example using tabsStyle.root (i.e. only changing how the classes prop is specified when rendering the Tab elements): https://codesandbox.io/s/overriding-a-withtheme-with-withstyle-hoc-ptj87
A separate issue in your sandbox is that you appear to be trying to specify style overrides in the theme in ConditionalThemeWrapper, but the structure of the theme is incorrect. The MuiFab and MuiTab entries in the theme should be within an overrides key. Here is a modified version of your sandbox demonstrating this: https://codesandbox.io/s/overriding-a-withtheme-with-withstyle-hoc-ju296
Related documentation:
https://material-ui.com/customization/components/#global-theme-override
I have a very niche use-case. I have to add a modal animation like this:
regular css animation
But I need to have a component (our own filter component for a datatable) inside said modal.
So I need to use the ModalService. But this service is only attaching my custom config like this:
toggleFilter = () => {
const modalOptions: ModalOptions = {
initialState: {
labels: this.datatableLabels, // needed for filter to have labels
filterGroups: this.filterGroups // needed to add filterGroups
},
class: 'filter-modal' // this sould be my custom class
};
this.bsModalRef = this.modalService.show(FilterComponent, modalOptions);
}
to modal-content and the above mentioned animation and styling uses divs above that. Not only it's working when encapsulation set toViewEncapsulation.None then it screws our other modals as well, since I cannot apply correct classes to the one I need to mess with.
How can I overcome this issue I'm having?
Instead of using the ModalService and open desired embedded component within the modal. You can basically inject the desired component into the body of the modal while using directive instead -> Here you are declaring the whole modal layout -> you can modify all the related classes so it's easier to modify a modal and have your ViewEncapsulation untuched so other modals are unaffected.
I have a modal that pops up with tab-able elements. Is there something like jQuery's :focusable or pointer-events: none that would allow me use CSS to disallow focusing on items underneath the overlay?
edit: The problem with a JS based approach (and selectors), is that each rerender with React, the query selector would have to be run through again. I don't have control over the component rendering this modal.
You want to do something along the lines of:
const inputs = [...document.getElementsByTagName('input')];
inputs.forEach((input) => input.setAttribute('tabindex', '-1'));
const modalInputs = [...document.querySelectorAll('.my-modal input')];
modalInputs.forEach((input, index) => input.setAttribute('tabindex', index));
Full Example https://codepen.io/bradevans/pen/GGYxrg
Edit: I hadn't seen the reactjs tag until OP commented... I'd utilize the Modals componentDidMount lifecycle method to change the underlying DOM inputs tab indexes and change the modals at the same time or using internal state....
You may also want to store the old tab indexes and reapply them on componentWillUnmount();
I have four classnames, lets call them red-0, red-1, blue-0, blue-1
The CSS for red-0 and red-1 is the same, and the CSS for blue-0 and blue-1 is the same.
My React component are changing the classNames just as I want. My problem is that it seems like the React component ignores the CSS styling when I apply a classname with the same CSS it had right before the state change. Is this an actual case? Because my CSS changes perfectly when it goes from e.g. red-0 to blue-0, but not from red-0 to red-1.
If you have a suggestion of how I can solve this problem, I would be greatful.
A buddy helped me:
The trix was to add a promise and first set the state to an empty classname, and then add the classname that i wanted inside the promise, like this:
this.setState({ className: '' }, () => this.setState({ className: 'red-0' }));
The reason for having four classnames instead of two was because I hoped the classname change would trigger a new animation, but it did not, even though it triggered new regular CSS changes. I ended up using only two classnames: red and blue