【问题标题】:React, how to update a state without repainting, or how to trigger useEffect without useState (and with useRef), or how to stop infinite loopReact,如何在不重新绘制的情况下更新状态,或者如何在没有 useState(和 useRef)的情况下触发 useEffect,或者如何停止无限循环
【发布时间】:2020-05-16 23:20:52
【问题描述】:

情况

  • 我想根据图像的高度重新分配网格(使用砌体方法)
  • 这些图片是延迟加载的

我想要什么

  • 加载图像时,它们会发送一个标志,该标志使砌体逻辑重新分配网格,每个人都很高兴
  • 然后当用户滚动时,加载更多图像,再次触发标志,砌体再次调整网格

会发生什么

* const [myState, setMyState] = use State(0)

  • 加载图像时我setMyState(1)
  • 在砌体逻辑中,我有一个useLayoutEffect(() =>{}, [myState]),所以当状态发生变化时,我会调整网格
  • 然后我用setMyState(0)重置状态,这样当加载新图像时它会再次触发useLayoutEffect
  • 但是,当我重置状态时,它会重新绘制图像,因此图像会再次加载并再次触发 useLayoutEffect,这会再次重置状态,从而创建无限循环
  • 如果我不重置状态,循环就会停止
  • 但是那么当滚动加载新图像时,setMyState(1) 不会改变任何内容,useLayoutEffect 也不会被触发

所以我现在卡住了

这是一个简化的代码

import React, { useRef, useLayoutEffect, useState} from "react";

const wait = ms =>
  new Promise((res, rej) => setTimeout(() => res("timed"), ms));

const Image = ({ setImageLoader }) => {
  // lazy load the image with an Observer, and then changes the state
  setImageLoader(1);
  return <></>;
};

export default function App() {
  const [imageLoaded, setImageLoader] = useState(0);

  useLayoutEffect(() => {
    const update_grid = async stillMounted => {
      // change stuff
      await wait(10000); // careful with crashing chrome
      console.log("updated");
      setImageLoader(0);
    };

    const stillMounted = { value: true };
    update_grid(stillMounted);
    return () => (stillMounted.value = false);
  }, [imageLoaded]);

  return <Image setImageLoader={setImageLoader} />;
}

尝试 1

我无法更改useLayoutEffect 中的状态,所以我只会在加载图像时更改内容,而不是再次进行更改

问题

当我滚动并加载更多图像时,我无法再触发 useLayoutEffect

import React, { useRef, useLayoutEffect, useState} from "react";

const wait = ms =>
  new Promise((res, rej) => setTimeout(() => res("timed"), ms));

const Image = ({ setImageLoader }) => {
  setImageLoader(1);
  return <>hey</>;
};

export default function App() {
  const [imageLoaded, setImageLoader] = useState(0);

  useLayoutEffect(() => {
    // change stuff
    const update_grid = async stillMounted => {
      await wait(40000);
      console.log("updated");
      // setImageLoader(0); NOW THIS IS NOT CAUSING THE REPAINT, BUT NEITHER WHEN NEW IMAGES ARE LOADED
    };

    const stillMounted = { value: true };
    update_grid(stillMounted);
    return () => (stillMounted.value = false);
  }, [imageLoaded]);

  return <Image setImageLoader={setImageLoader} />;
}

尝试 2

如果我不想重绘,但想触发 useLayoutEffect,我可以使用 useRef 而不是 useState

import React, { useRef, useLayoutEffect, useState, useEffect } from "react";

const wait = ms =>
  new Promise((res, rej) => setTimeout(() => res("timed"), ms));

const Image = ({ imageLoaded }) => {
  imageLoaded.current++;
  return <>hey</>;
};

export default function App() {
  const imageLoaded = useRef(0);

  useLayoutEffect(() => {
    // change stuff
    const update_grid = async stillMounted => {
      await wait(10000);
      console.log("updated " + imageLoaded.current);
      imageLoaded.current++;
    };

    const stillMounted = { value: true };
    update_grid(stillMounted);
    return () => (stillMounted.value = false);
  }, [imageLoaded.current]);

  return <Image imageLoaded={imageLoaded} />;
}

但codeandbox 已经警告我imageLoaded.current 不会导致组件重新渲染(即使我或多或少想要这个?)

所以最后,我看到 imageLoaded.current 在加载新图像时增加,但 useLayoutEffect 没有被触发

【问题讨论】:

    标签: reactjs react-hooks use-effect use-state


    【解决方案1】:

    我无法使用useEffect 或类似方法解决这个问题

    相反,我使用了一个简单的函数来调整网格,并在加载图像时从延迟加载逻辑中调用它

    import React, { useRef, useEffect, useState } from "react";
    
    const wait = ms =>
      new Promise((res, rej) => setTimeout(() => res("timed"), ms));
    
    const Image = ({ adjustMasonry }) => {
      // lazy load the image with an Observer, and then changes the state
      const scroll = async () => {
        adjustMasonry();
        adjustMasonry(); // only causes one update due to the implemented buffer
        await wait(4000);
        adjustMasonry();
      };
      scroll();
      return <></>;
    };
    
    export default function App() {
      let signals = 0;
      const adjustMasonry = async stillMounted => {
        signals++;
        const last_signal = signals;
        await wait(200); // careful with crashing chrome
        if (signals !== last_signal) return; // as a way to minimize the number of consecutive calls
        if (stillMounted && !stillMounted.value) return;
    
        // change stuff
        console.log("updated");
      };
    
      useEffect(() => {
        const stillMounted = { value: true };
        adjustMasonry(stillMounted);
    
        return () => {
          stillMounted.value = false;
        };
      }, []);
    
      return <Image adjustMasonry={adjustMasonry} />;
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多