使用 React Hooks 进行动态媒体查询
Dynamic media query with React Hooks
我正在尝试在 React 基础上构建一个 Bootstrap 网格(使用 Container
、Row
、col-md-12
等)。我的第一个问题是 Container
。在 Bootstrap 中,Container
有多个媒体查询。我可以(我将 Styled Components
与 Styled Tools
一起使用)显然可以创建多个媒体查询,这些媒体查询将相互覆盖,但我知道这不是最好的方法。
我正在考虑创建一种方法来检查用户 window.innerWidth
并将其更改为 useEffect
但我在开发自己的逻辑时遇到了一些问题,而且这是否会影响性能.这是我目前所拥有的。
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) => {
return `@media(min-width: ${minWidth}px) {
max-width: ${maxWidth}px;
}`;
};
const StyledContainer = styled.div`
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
${ifNotProp(
"fluid",
css`
${mediaQuerie(576, 540)}/* WRONG WAY USING MEDIA QUERIES */
${mediaQuerie(768, 720)}
${mediaQuerie(992, 960)}
${mediaQuerie(1200, 1140)}
`
)}
`;
const Container = props => {
const [width, setWidth] = useState();
setWidth(window.innerWidth);
useEffect(() => {
console.log(window.addEventListener("resize", width));
}, [width]);
return <StyledContainer {...props} />;
};
Container.propTypes = {
children: PropTypes.node,
fluid: PropTypes.bool
};
Container.defaultProps = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200
};
export default Container;
在我看来,针对不同的屏幕尺寸使用多个媒体查询总是更好。使用开箱即用的东西比自定义构建的逻辑具有性能提升。
如果您仍想采用相反的方式,即 使用 resize
侦听器 根据屏幕尺寸应用动态样式。您可以按如下方式调整代码。
确保只添加一次事件侦听器。为此,您的 useEffect 应更改为
useEffect(() => {
setWidth(window.innerWidth);
window.addEventListener("resize", () => setWidth(window.innerWidth));
}, []);
- 第一行
setWidth(window.innerWidth);
设置宽度
初始渲染,因为在那一刻调整大小事件永远不会
触发。
- 第二行
window.addEventListener("resize", () => setWidth(window.innerWidth));
注册监听器更新状态(宽度)
- 传递给useEffect的空数组[]确保事件监听器
只添加一次
现在创建一个接受宽度和 returns 样式道具的函数。例如,我创建了一个 function
fluidMediaQuery,它向样式容器添加背景颜色。
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");
}
};
您的样式组件现在可以采用 fluid
道具并以不同方式呈现。
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) : "")}
`
)}
`;
最后渲染组件 <StyledContainer fluid={true} width={width} />
这里的宽度来自状态,由调整大小事件侦听器设置。
完成的代码可能如下所示。尝试调整屏幕大小,您应该能够看到背景颜色发生变化。
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;
重要说明。我不确定是否在所有情况下都会触发调整大小。例如,如果移动设备上的方向发生变化,是否会触发?您可能需要对这些方面进行一些研究。下面的 post 可能会有所帮助。
Will $(window).resize() fire on orientation change?
我正在尝试在 React 基础上构建一个 Bootstrap 网格(使用 Container
、Row
、col-md-12
等)。我的第一个问题是 Container
。在 Bootstrap 中,Container
有多个媒体查询。我可以(我将 Styled Components
与 Styled Tools
一起使用)显然可以创建多个媒体查询,这些媒体查询将相互覆盖,但我知道这不是最好的方法。
我正在考虑创建一种方法来检查用户 window.innerWidth
并将其更改为 useEffect
但我在开发自己的逻辑时遇到了一些问题,而且这是否会影响性能.这是我目前所拥有的。
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) => {
return `@media(min-width: ${minWidth}px) {
max-width: ${maxWidth}px;
}`;
};
const StyledContainer = styled.div`
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
${ifNotProp(
"fluid",
css`
${mediaQuerie(576, 540)}/* WRONG WAY USING MEDIA QUERIES */
${mediaQuerie(768, 720)}
${mediaQuerie(992, 960)}
${mediaQuerie(1200, 1140)}
`
)}
`;
const Container = props => {
const [width, setWidth] = useState();
setWidth(window.innerWidth);
useEffect(() => {
console.log(window.addEventListener("resize", width));
}, [width]);
return <StyledContainer {...props} />;
};
Container.propTypes = {
children: PropTypes.node,
fluid: PropTypes.bool
};
Container.defaultProps = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200
};
export default Container;
在我看来,针对不同的屏幕尺寸使用多个媒体查询总是更好。使用开箱即用的东西比自定义构建的逻辑具有性能提升。
如果您仍想采用相反的方式,即 使用 resize
侦听器 根据屏幕尺寸应用动态样式。您可以按如下方式调整代码。
确保只添加一次事件侦听器。为此,您的 useEffect 应更改为
useEffect(() => {
setWidth(window.innerWidth);
window.addEventListener("resize", () => setWidth(window.innerWidth));
}, []);
- 第一行
setWidth(window.innerWidth);
设置宽度 初始渲染,因为在那一刻调整大小事件永远不会 触发。 - 第二行
window.addEventListener("resize", () => setWidth(window.innerWidth));
注册监听器更新状态(宽度) - 传递给useEffect的空数组[]确保事件监听器 只添加一次
现在创建一个接受宽度和 returns 样式道具的函数。例如,我创建了一个 function
fluidMediaQuery,它向样式容器添加背景颜色。
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");
}
};
您的样式组件现在可以采用 fluid
道具并以不同方式呈现。
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) : "")}
`
)}
`;
最后渲染组件 <StyledContainer fluid={true} width={width} />
这里的宽度来自状态,由调整大小事件侦听器设置。
完成的代码可能如下所示。尝试调整屏幕大小,您应该能够看到背景颜色发生变化。
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;
重要说明。我不确定是否在所有情况下都会触发调整大小。例如,如果移动设备上的方向发生变化,是否会触发?您可能需要对这些方面进行一些研究。下面的 post 可能会有所帮助。
Will $(window).resize() fire on orientation change?