Hey! I'm on a mission to make 100 React.js projects ending March 8th. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!
Link to today's deployed app: Link
Link to the repo: github
This was a short project riding the heels of the first draft of a client website. The website is optimized for mobile but we wanted to just put a couple of styles in so that it's comprehensible to desktop viewers. The primary challenge was the static background image element saying (paraphrased from Spanish) "More coming soon".
For mobile screens I wanted to take advantage of the background-size: cover
CSS property to simply cover the space. On quite large screens though this can look ridiculous since only maybe a small part of the image will be showing.
In pure CSS this is easily solved by a media query. Simply state the screen size you are optimizing for (say- over 570px wide) and set the styles within the brackets.
In this project I am actually making use of props
to pass an image URL to the component so that the component is reusable. Since I'm setting the CSS background
property within the JSX, it would override some of the other styles in actual stylesheet.
The CSS is loaded in the
of the html page well before the image URL is passed to this object so this got in the way of me being able to give the image thebackground-size: cover
style.
Instead, we can use Javascript to check for the screen size with the following built-in vanilla Javascript property of the window
object:
window.innerWidth
This property returns the inner width of the window thus allowing you to set styles for different screen sizes.
The problem? Well this property is only called once on page load. Frankly for mobile this is just fine, but if someone is looking at it on a browser and decides to resize the window and push it to the side, it won't be called again. To call the property again when the window is resized we take advantage of a callback on the window.onresize
method. I am using React useState
to set a windowWidth variable to the size of the screen on page load and whenever the screen is resized.
const [windowWidth,setWindowWidth] = useState(0);
window.onresize = () => {
setWindowWidth(window.innerWidth);
}
Now that we have a variable that updates whenever the screen changes sizes we can create separate style objects to pass to the JSX element for each screen size, like here, where I use a ternary:
const innerImageStyle = windowWidth < 570 ?
({
width: '100%',
height: '80vh',
backgroundImage: `url(${props.imageUrl})`,
backgroundSize: 'cover',
}) :
({
width: '100%',
height: '500px',
backgroundImage: `url(${props.imageUrl})`,
backgroundSize: 'contain',
});
As you can see if it's a mobile device (less than 570 pixels wide) then we want the background to cover the space available. If it's a desktop computer, we want the background to repeat (client's choice here but I like it).
I'm sure we could also do this using the React useEffect hook, but this is intended to be a simple version and I honestly feel like the built-in Javascript window methods are underestimated and under-taught for how powerful they are.
Let me know what you think and how you do conditional styling for React components in the comments...
Top comments (12)
I have to give my honest opinion. This is extremely bad. First of all you should never use objects for styling because whenever React runs the reconciliation algorithm to detect changes, it will obviously make a new copy of the virtual DOM. This means it will create a new object and compare references. It will re-render the component each time and not only when it hits the media query.
Stick to CSS/SCSS!! Or simply use a library like JSS or styled components instead (for dynamic styling). Maybe you are a beginner, which is okay, you are trying things but just keep these things in mind. Keep hustling!
No worries Muhammad I’m trying to do a react project each day so obviously going for quantity over quality. Others have complained about not having useEffect in the component which would obviously re-render the component less often.
Just curious about what you said-
“ whenever React runs the reconciliation algorithm to detect changes, it will obviously make a new copy of the virtual DOM”- does it make a whole new copy of the virtual dom or does it just re-render the component? I thought one of the main value adds of React is that it won’t re render the whole dom when it detects changes in a component.
Virtual DOM is an in memory representation of the actual DOM. React creates a second virtual DOM and both of these exist in memory at this point, after "diffing," it only updates the part of actual browser DOM that needs to be updated.
Also, about not using useEffect, each time this component is re-rendered, the code actually keep on adding more event listeners. This is a very good example of memory leak. :D
One thing I don't understand is if I don't write a stylesheet in JSX, what if I write in Sass or Css? What is the difference between them?
Hey Ratul- You can and should have separate style sheets. You import them at the top of the page like so:
import React from “react”;
import “./myStyles.css”;
My blog post is just if you want dynamic styles and can’t use media queries :)
And even then, most of the time you just change the class. You would write the styles for two classes in your style sheet like “.dark-mode-on” and “.dark-mode-off” and then in your JS file toggle between giving the JSX element one of the two classes.
Worst thing about react is that styles are effected from any CSS files that matches the elements even if it's not from the CSS intented for that component. Like style leak. And we have to resort to styled components and crap. Is there a way to tie a CSS to a component without leaks?
Yes, there is an easy way, you can do CSS modules, (styles.module.css) that way it'll only take the styles from the specified module even if another module had the same class name.
When you declare a className on a jsx component it goes like this example:
className ={module1.btn}
It'll only apply the styles for btn that are declared on the module1 module
oh got it. Thanks a lot for explaining brother :)
window.onresize gonna fire every time if you can incorporate debouncing that would be cool
You mean on every resize or every component load? I think either one is ok. I’ll check into this though- maybe you’re right.
yup it impact the website performance also
but there is another cool thing Resize observer
developer.mozilla.org/en-US/docs/W...
check this one out
Would love to see the same or similar thing done with useEffect hook 😁