In my opinion it's always better to use multiple media queries to target different screen sizes. Using something available out of the box would have performance gains than a custom built logic.
If you still want to go the other way around, ie Apply dynamic styles based on screen size using resize
listener. You could tweak your code as follows.
Make sure event listeners are added only once. For this your useEffect should change to
useEffect(() => {
setWidth(window.innerWidth);
window.addEventListener("resize", () => setWidth(window.innerWidth));
}, []);
- The first line
setWidth(window.innerWidth);
sets the width on
initial render, because at that moment the resize event is never
triggered.
- The second line
window.addEventListener("resize", () => setWidth(window.innerWidth));
registers the listener to update the state (width)
- The empty array [] passed to useEffect makes sure the event listener
is added only once
Now create a function that accepts the width and returns the style props. For example I have created a function
fluidMediaQuery that adds a background color to the styled container.
const mediaQuerie = (minWidth, maxWidth, color) => {
return `@media(min-width: ${minWidth}px) {
max-width: ${maxWidth}px;
background-color: ${color}
}`;
};
const fluidMediaQuery = width => {
if (width > 1200) {
return mediaQuerie(1140, 1200, "green");
} else if (width > 992) {
return mediaQuerie(992, 960, "blue");
} else if (width > 768) {
return mediaQuerie(720, 768, "red");
} else {
return mediaQuerie(540, 576, "yellow");
}
};
Your styled component can now take a fluid
prop and render differently.
const StyledContainer = styled.div`
width: 100%;
height: 100px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
${ifNotProp(
"fluid",
css`
${mediaQuerie(540, 576)}/* WRONG WAY USING MEDIA QUERIES */
${mediaQuerie(720, 768)}
${mediaQuerie(960, 992)}
${mediaQuerie(1140, 1200)}
`
)}
${ifProp(
"fluid",
css`
${props => (props.width ? fluidMediaQuery(props.width) : "")}
`
)}
`;
And finally render the component <StyledContainer fluid={true} width={width} />
Here width is coming from the state, which is set by resize event listener.
Completed code might look like below. Try resizing the screen and you should be able to see the background color change.
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
import { ifProp, ifNotProp } from "styled-tools";
const mediaQuerie = (minWidth, maxWidth, color) => {
return `@media(min-width: ${minWidth}px) {
max-width: ${maxWidth}px;
background-color: ${color}
}`;
};
const fluidMediaQuery = width => {
if (width > 1200) {
return mediaQuerie(1140, 1200, "green");
} else if (width > 992) {
return mediaQuerie(992, 960, "blue");
} else if (width > 768) {
return mediaQuerie(720, 768, "red");
} else {
return mediaQuerie(540, 576, "yellow");
}
};
const StyledContainer = styled.div`
width: 100%;
height: 100px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
${ifNotProp(
"fluid",
css`
${mediaQuerie(540, 576)}/* WRONG WAY USING MEDIA QUERIES */
${mediaQuerie(720, 768)}
${mediaQuerie(960, 992)}
${mediaQuerie(1140, 1200)}
`
)}
${ifProp(
"fluid",
css`
${props => (props.width ? fluidMediaQuery(props.width) : "")}
`
)}
`;
const Container = props => {
const [width, setWidth] = useState();
useEffect(() => {
setWidth(window.innerWidth);
window.addEventListener("resize", () => setWidth(window.innerWidth));
}, []);
return (
<div>
<h4>Width of screen: {width}</h4>
<StyledContainer fluid={true} width={width} />
</div>
);
};
Container.propTypes = {
children: PropTypes.node,
fluid: PropTypes.bool
};
Container.defaultProps = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200
};
export default Container;
Important note. I'm not sure if resize is triggered in all cases. For example in case of orientation change on a mobile device, would this be triggered or not?. You might need to do some research on those aspects. The below post might help.
Will $(window).resize() fire on orientation change?