how to have react wait until load with react / jsx for styling? - css

i'm consistently having this issue where with js/jsx/react, i am consistently running into this issue where my logic is running before the page loads, and because of that, i am consistently running into errors and what not as my code trys to execute before my page has finished loading resulting in a tonne of errors.
how do i consistently prevent this/prevent myself from running into these issues?
i've tried using a bunch of window.onload() functions but that feels wrong and i feel like there should be a better way to do so that' i'm unable to find with googling or on forms.
// an example of my issue is the following code consistently errors:
function Title() {
var text = "Hello";
var arr = [];
//convert word into an array of chars
for (let i=0; i<=text.length; i++){
console.log("loop")
arr.push(text.charAt(i));
}
//output each letter into a span
const listItems = arr.map((number) => <span>{number}</span>);
//neither of these work
// document.getElementById('title').style.color = ('rgba(0, 0, 0, 0.0)');
// document.getElementById('title').style.color = "#ffFFff"
return (
<h1 id='title'>{listItems}</h1>
)
}
the error i'm commonly getting is that react is claiming that the style i'm trying to change/access jsx claims "do not exists", but if i try change it in the console in the browser, it works fine.

To add inline styles to a react element you should use the style prop. This way:
<h1 id="title" style={{ color: "#fff" }}>{listItems}</h1>
You can't get access to the DOM element which is being rendered on the body of the component, you should instead use a ref and an useEffect for that purpose.

Related

change global css element

i've made this banner like screen that appears when my site is loaded, but here's the thing, i don't want no scrollbar while this opening animation it's happening, i only want to show the other components (the scrollbar and the whole site) once the gsap animation finishes, how could i proceed? thanks! (i tried to create a function to control those global elements, is it a way?)
So if I understand correctly you need the Banner to be displayed until the site is loaded. Maybe you are making some API calls or in general, you are planning to show the banner for let's say 3 sec and post that you want your actual components to be displayed.
You can try below approch:
export const APP = (): JSX.Element => {
const [isAnimationInProgress, SetAnimationState] = React.useState(true);
React.useEffect(() => {
// You can have your page load API calls done here
// Or wait for 'X' seconds
// Post that set the AnimationState to false to render actual components
setAnimationState(false);
})
return (
{
isAnimationInProgress && <Banner />
}
{
!isAnimationInProgress && <ActualComponent />
}
)
}
Regarding scrollbars, including overflow: hidden; in style for the banner should do the work if you are getting scrollbars for the Banner component.

Nextjs: server rendered code blocks highlighted by Prismjs mismatches and causes re-render due to leading whitespace on class attribute

I am trying to implement server-side rendering of syntax highlighted (tokenized) code blocks using Prismjs (note: I know how to do this using client-side rendering via useEffect and refs, and I got this working using prism-react-renderer. I am looking specifically for the solution for "bare" Prismjs & SSR).
// codeExamples = array of some code strings
const Code = ({ code, language }) => {
const html = Prism.highlight(code, Prism.languages[language], language);
return (
<pre
data-language={language}
className={`language-${language}`}
>
<code
dangerouslySetInnerHTML={{
__html: html,
}}
/>
</pre>
);
};
export default function Home() {
return codeExamples.map((example, i) => (
<Code
language="javascript"
code={example}
key={i}
></Code>
));
}
It does work to some extent, but I've run into an issue: the code block gets briefly re-rendered, probably because of a leading whitespace on the class attribute:
1st render: class="language-javascript"
2nd render: class=" language-javascript
This is causing (besides the poinless expensive re-render) a non-pleasant layout shift, temporarily fixed by adding hard-coded font-size in pixels to a <pre> element.
I've got some occasional warnings, either for server and client props mismatch (can't reproduce right now) or for Extra attributes from the server: class – but only when running next dev, not while running next build && next start.
Tested on the latest versions of Nextjs and Prism.
I've got an answer from a dive into Prismjs source code:
if (parent && parent.nodeName.toLowerCase() === 'pre') {
parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
}
This is where the whitespace comes from. It is quite obvious, why it is here (as a padding for string concatenation).
A temporary hacky fix is to add a leading whitespace to a className on <pre> element as well.
<pre
data-language={language}
className={` language-${language}`}
>
But it is quite clear that this is not a good way to use Prismjs for SSR.

How would I apply Material-UI managed styles to non-material-ui, non-react elements?

I have an application where I'm using Material UI and its theme provider (using JSS).
I'm now incorporating fullcalendar-react, which isn't really a fully fledged React library - it's just a thin React component wrapper around the original fullcalendar code.
That is to say, that I don't have access to things like render props to control how it styles its elements.
It does however, give you access to the DOM elements directly, via a callback that is called when it renders them (eg. the eventRender method).
Here's a basic demo sandbox.
Now what I'm wanting to do is make Full Calendar components (eg, the buttons) share the same look and feel as the rest of my application.
One way to do this, is that I could manually override all of the styles by looking at the class names it's using and implementing the style accordingly.
Or - I could implement a Bootstrap theme - as suggested in their documentation.
But the problem with either of these solutions, is that that:
It would be a lot of work
I would have synchronisation problems, if I made changes to my MUI theme and forgot to update the calendar theme they would look different.
What I would like to do is either:
Magically convert the MUI theme to a Bootstrap theme.
Or create a mapping between MUI class names and the calendar class names, something like:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
I wouldn't mind having to massage the selectors etc to make it work (ie. For example - MUI Buttons have two internal spans, whereas Full Calendar have just one). It's mostly about when I change the theme - don't want to have to change it in two places.
Using something like Sass with its #extend syntax would is what I have in mind. I could create the full-calendar CSS with Sass easily enough - but how would Sass get access to the MuiTheme?
Perhaps I could take the opposite approach - tell MUI 'Hey these class names here should be styled like these MUI classes'.
Any concrete suggestions on how I would solve this?
Here is my suggestion (obviously, it's not straight forward). Take the styles from the MUI theme and generate style tag based on it using react-helmet. To do it event nicely, I created a "wrapper" component that do the map. I implemented only the primary rule but it can be extended to all the others.
This way, any change you will do in the theme will affect the mapped selectors too.
import React from "react";
import { Helmet } from "react-helmet";
export function MuiAdapter({ theme }) {
if (!theme.palette) {
return <></>;
}
return (
<Helmet>
<style type="text/css">{`
.fc-button-primary {
background: ${theme.palette.primary.main}
}
/* more styles go here */
`}</style>
</Helmet>
);
}
And the use of the adapter
<MuiAdapter theme={theme} />
Working demo: https://codesandbox.io/s/reverent-mccarthy-3o856
You could create a mapping between MUI class names and the calendar class names by going through ref's. It's possible that this is not what some would call "best practice"...but it's a solution :). Note that I updated your component from a functional component to a class component, but you could accomplish this with hooks in a functional component.
Add refs
Add a ref to the MUI element you want to set as a reference, in your case the Button.
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
And a ref to a wrapping div around the component you want to map to. You can't add it directly to the component since that wouldn't give us access to children.
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
Map classes
From componentDidMount() add whatever logic you need to target the correct DOM node (for your case, I added logic for type and matchingClass). Then run that logic on all FullCalendar DOM nodes and replace the classList on any that match.
componentDidMount() {
this.updatePrimaryBtns();
}
updatePrimaryBtns = () => {
const children = Array.from(this.fullCal.children);
// Options
const type = "BUTTON";
const matchingClass = "fc-button-primary";
this.mapClassToElem(children, type, matchingClass);
};
mapClassToElem = (arr, type, matchingClass) => {
arr.forEach(elem => {
const { tagName, classList } = elem;
// Check for match
if (tagName === type && Array.from(classList).includes(matchingClass)) {
elem.classList = this.primaryBtn.classList.value;
}
// Run on any children
const next = elem.children;
if (next.length > 0) {
this.mapClassToElem(Array.from(next), type, matchingClass);
}
});
};
This is maybe a little heavy handed, but it meets your future proof requirement for when you updated update Material UI. It would also allow you to alter the classList as you pass it to an element, which has obvious benefits.
Caveats
If the 'mapped-to' component (FullCalendar) updated classes on the elements you target (like if it added .is-selected to a current button) or adds new buttons after mounting then you'd have to figure out a way to track the relevant changes and rerun the logic.
I should also mention that (obviously) altering classes might have unintended consequences like a breaking UI and you'll have to figure out how to fix them.
Here's the working sandbox: https://codesandbox.io/s/determined-frog-3loyf

Unload/remove dynamically loaded css files

After loading a css file like this:
const themes = ['dark-theme.css', 'light-theme.css'];
async function loadcss(file) {
return await import(file);
}
loadcss(themes[0]).then(console.log)
The console output is an empty object for me and a new annonymous < style> tag sits in the < head> of my index.html. So far so good, but what if I (in this example) want to change the theme to light-theme.css. That would merge both themes as dark-theme.css is already loaded.
Is there a way to remove the < style> tag from the DOM?
To furthermore specify my question, the provided example shows an abstracted behaviour and I am only interested in removing the dynamically loaded css from the DOM.
I don't know vue.js but here is simple example in React hope it helps somehow :) perhaps some ideas at least :)
class TodoApp extends React.Component {
static themes = {
dark: 'dark-theme.css',
light: 'light-theme.css',
};
render() {
return ReactDOM.createPortal(
(<link rel="stylesheet" href={TodoApp.themes.dark} type="text/css"></link>),
document.head
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
http://jsfiddle.net/3y4hw2ox/
Thanks to OZZIE, I questioned my methodology and found, that importing the css files, like my question shows (through ES6 import, or require.context(...)), is not usefull, as we can't identify it, we dont get access to the <style> element, leaving us with no entry to the DOM and no way to manipulate it.
Instead we will link the css files manually in the <head>, as we know their name and path.
const themes = ['dark-theme.css', 'light-theme.css'];
const head = document.body.parentElement.firstElementChild;
const link = document.createElement('link');
link.setAttribute('href', process.env.BASE_URL + themes[0]);
link.setAttribute('id', themes[0]); // set id so we can remove it later
head.appendChild(link);

Generating inline font-size style using ReactJS

I am trying to do something like this in ReactJS:
var MyReactClass = React.createClass({
render: function() {
var myDivText = "Hello!";
var myFontSize = 6; //this is actually something more complicated, I'm calculating it on the fly
var divStyle = {
font-size: {fontSize + 'px !important;'},
};
return (<div style={divStyle}>{myDivText}</div>);
}
});
The problem is that I get this error when I run my code:
"Module build failed: Error: Parse Error: Line 5: Unexpected token -"
apparently, React doesn't like that font-size has a dash in it. How do I get around this? Is there some way to escape that character for react? Is there some different css property that react likes better that does the same thing?
Thanks!
Use fontSize instead of font-size
the solution is to camelCase properties which usually contain a dash
http://facebook.github.io/react/tips/inline-styles.html
Answered my own question :)
I use fontSize: pixels numbers
As https://reactjs.org/docs/dom-elements.html says,
We need to remove '-' and upperCase except first word
Example-
background-color as backgroundColor,
Same will be applicable everywhere except a few as-
aria-* and data-*
example-
aria-label as aria-label
Above worked for me!

Resources