简体   繁体   English

使用 React Hooks 进行动态媒体查询

[英]Dynamic media query with React Hooks

i'm trying to build a Bootstrap Grid(with Container , Row , col-md-12 , etc) from ground on React.我正在尝试在 React 上从地面构建一个引导网格(带有ContainerRowcol-md-12等)。 My first problem is with Container .我的第一个问题是Container In Bootstrap, Container has multiple media queries.在 Bootstrap 中, Container有多个媒体查询。 I could(i'm using Styled Components with Styled Tools ) obviously create multiple media queries that will overwrite each other but i know this is not the best way.我可以(我正在使用Styled ComponentsStyled Tools )显然创建多个将相互覆盖的媒体查询,但我知道这不是最好的方法。

I was thinking on creating a way to check user window.innerWidth and change it with useEffect but i'm having some problems on developing my own logic and also if this is going to hurt performance.我正在考虑创建一种方法来检查用户window.innerWidth并使用useEffect更改它,但我在开发自己的逻辑时遇到了一些问题,以及这是否会损害性能。 This is what i have so far.这就是我到目前为止所拥有的。

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;

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 .如果您仍然想 go 反过来,即使用resize listener 根据屏幕大小应用动态 styles You could tweak your code as follows.您可以按如下方式调整您的代码。

Make sure event listeners are added only once.确保仅添加一次事件侦听器。 For this your useEffect should change to为此,您的 useEffect 应更改为

useEffect(() => {
    setWidth(window.innerWidth);
    window.addEventListener("resize", () => setWidth(window.innerWidth));
  }, []);
  • The first line setWidth(window.innerWidth);第一行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));第二行window.addEventListener("resize", () => setWidth(window.innerWidth)); registers the listener to update the state (width)注册监听器以更新 state(宽度)
  • The empty array [] passed to useEffect makes sure the event listener is added only once传递给 useEffect 的空数组 [] 确保事件监听器只添加一次

Now create a function that accepts the width and returns the style props.现在创建一个接受宽度并返回样式道具的 function。 For example I have created a function fluidMediaQuery that adds a background color to the styled container.例如,我创建了一个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");
  }
};

Your styled component can now take a fluid prop and render differently.您的样式化组件现在可以采用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) : "")}
    `
  )}
`;

And finally render the component <StyledContainer fluid={true} width={width} /> Here width is coming from the state, which is set by resize event listener.最后渲染组件<StyledContainer fluid={true} width={width} />这里宽度来自 state,由 resize 事件监听器设置。

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? $(window).resize() 会在方向改变时触发吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM