【问题标题】:useEffect hook not firing on orientation changeuseEffect 挂钩未在方向更改时触发
【发布时间】:2020-02-12 08:06:04
【问题描述】:

我正在尝试更改容器的高度,仅在移动横向模式下。我在开发人员工具中玩转移动设备的方向,但它只适用于第一次渲染。我是反应钩子的新手,所以不确定我是否正确实施。

我的想法是,我在横向测试一次,如果它在移动设备上,高度应该小于 450 像素(这是我为 if 语句所做的检查)

请有人指出我正确的方向吗?

谢谢!

const bikeImageHeight = () => {
    const windowViewportHeight = window.innerHeight;
    const isLandscape = window.orientation === 90 || window.orientation === -90;
    let bikeImgHeight = 0;

    if (windowViewportHeight <= 450 && isLandscape) {
      bikeImgHeight = windowViewportHeight - 50;
    }

    return bikeImgHeight;
  };

  useEffect(() => {
    bikeImageHeight();

    window.addEventListener("resize", bikeImageHeight);

    return () => {
      window.removeEventListener("resize", bikeImageHeight);
    };
  }, []);

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    这是一个在方向改变时触发的自定义钩子,

    import React, {useState, useEffect} from 'react';
    
    // Example usage
    export default () => {
      const orientation = useScreenOrientation();
      return <p>{orientation}</p>;
    }
    
    function useScreenOrientation() {
      const [orientation, setOrientation] = useState(window.screen.orientation.type);
    
      useEffect(() => {
        const handleOrientationChange= () => setOrientation(window.screen.orientation.type);
        window.addEventListener('orientationchange', handleOrientationChange);
        return () => window.removeEventListener('orientationchange', handleOrientationChange);
      }, []);
    
      return orientation;
    }
    

    希望这能带你走向正确的方向。

    【讨论】:

    • 太棒了!我只是将定义handleOrientationChange 放在useEffect 之外
    • @IProblemFactory,这不会有太大变化,因为useEffect 回调只会被调用一次。
    【解决方案2】:

    useEffect 钩子预计不会在方向更改时触发。它定义了一个回调,该回调将在组件重新渲染时触发。那么问题是如何在屏幕方向改变时触发重新渲染。当组件道具或状态发生更改时,会发生重新渲染。

    让我们利用另一个related stackoverflow answer 来构建一个useWindowDimensions 钩子。这允许我们将窗口大小作为组件状态挂钩,因此任何更改都会导致重新渲染。

    使用WindowDimensions.js

    import { useState, useEffect } from 'react';
    
    function getWindowDimensions() {
      const { innerWidth: width, innerHeight: height } = window;
      return {
        width,
        height
      };
    }
    
    export default function useWindowDimensions() {
      const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
    
      useEffect(() => {
        function handleResize() {
          setWindowDimensions(getWindowDimensions());
        }
    
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
      }, []);
    
      return windowDimensions;
    }
    

    然后您可以在组件中使用该挂钩。比如:

    BikeImage.js

    import React from 'react'
    import useWindowDimensions from './useWindowDimensions'
    
    export default () => {
      const windowDimensions = useWindowDimensions();
    
      // Define these helper functions as you like
      const width = getImageWidth(windowDimensions.width)
      const height = getImageHeight(windowDimensions.height)
    
      // AppImage is a component defined elsewhere which takes 
      // at least width, height and src props
      return <AppImage  width={width} height={height} src="..." .../>
    }
    

    【讨论】:

      【解决方案3】:

      您需要触发重新渲染,这可以通过在 bikeImageHeight 中设置状态来完成。

        const [viewSize, setViewSize] = useState(0)
      
      
          const bikeImageHeight = () => {
              const windowViewportHeight = window.innerHeight;
              const isLandscape = window.orientation === 90 || window.orientation === -90;
              let bikeImgHeight = 0;
      
              if (windowViewportHeight <= 450 && isLandscape) {
                bikeImgHeight = windowViewportHeight - 50;
              }
              setViewSize(bikeImgHeight)
              return bikeImgHeight;
        };
      

      根据 cmets 的对话,以下是您使用 debounce 的方法:

      function debounce(func, wait, immediate) {
          var timeout;
          return function() {
              var context = this, args = arguments;
              var later = function() {
                  timeout = null;
                  if (!immediate) func.apply(context, args);
              };
              var callNow = immediate && !timeout;
              clearTimeout(timeout);
              timeout = setTimeout(later, wait);
              if (callNow) func.apply(context, args);
          };
      };
      
      
      const YourComponent = () => {
      
        const bikeImageHeight = () => {
          const windowViewportHeight = window.innerHeight;
          const isLandscape = window.orientation === 90 || window.orientation === -90;
          let bikeImgHeight = 0;
      
          if (windowViewportHeight <= 450 && isLandscape) {
            bikeImgHeight = windowViewportHeight - 50;
          }
          setViewSize(bikeImgHeight)
          return bikeImgHeight;
        };
      
      
        const debouncedBikeHeight = debounce(bikeImageHeight, 200)
      
        useEffect(() => {
          bikeImageHeight();
      
          window.addEventListener("resize", debouncedBikeHeight);
      
          return () => {
            window.removeEventListener("resize", debouncedBikeHeight);
          };
        }, []);
      
        return <div>some jsx</div>
      
      }
      

      从此处获取的去抖动示例:https://davidwalsh.name/javascript-debounce-function

      【讨论】:

      • 您需要限制或消除您的事件。 codeburst.io/…
      • 我在将函数向下传递到我已删除的子组件时不小心直接调用了该函数,但是从纵向切换到横向时,bikeImageHeight 仍然没有注销它的值,反之亦然?
      • 您的控制台日志在哪里?它在bikeImageHeight里面吗?
      • 我目前正在使用 useEffect();如果我在 useEffect() 之外登录,我会再次收到循环错误:(我需要将 bikeImageHeight 的值传递给组件,但我需要在方向更改时更改值
      • 啊,明白了,是的,useEffect 不会多次触发。如果它没有“观察”第二个参数数组中的变量,它的行为就像经典的“componentDidMount”生命周期方法。我将更新我的帖子以演示如何在您的视口大小更改时触发 useEffect。
      猜你喜欢
      • 2020-08-03
      • 1970-01-01
      • 2022-10-21
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      • 2021-06-29
      • 2020-09-07
      • 2019-07-31
      相关资源
      最近更新 更多