Реагируйте, как обновить состояние без перерисовки, или как вызвать useEffect без useState (и с useRef), или как остановить бесконечный цикл - PullRequest
0 голосов
/ 31 января 2020

Ситуация

  • Я хочу перераспределить сетку на основе высоты изображений (используя подход кладки)
  • Эти изображения загружены с отложенной загрузкой

Что я хочу

  • Когда изображения загружаются, они отправляют флаг, этот флаг заставляет кладку логи c перераспределять сетку, и все счастливы
  • Затем, когда пользователь прокручивает, загружаются другие изображения, снова запускает флаг и кладка снова настраивает сетку

Что происходит

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

  • Когда загружаются изображения I setMyState(1)
  • В кирпичной логике c у меня есть useLayoutEffect(() =>{}, [myState]), поэтому при изменении состояния я настраиваю сетку
  • Затем я сбрасываю состояние с setMyState(0), так что при загрузке нового изображения оно снова будет вызывать useLayoutEffect
  • НО , когда я сбрасываю состояние, оно перерисовывает изображения, поэтому изображения загружаются снова и снова вызвать useLayoutEffect, который снова сбрасывает состояние, создавая бесконечное l oop
  • Если я не сбрасываю состояние, l oop останавливается
  • НО , тогда, когда новое изображение загружается с помощью прокрутки, 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} />;
}

Но codesandbox уже предупреждает меня, что imageLoaded.current не вызовет повторную визуализацию компонента (даже если я более или менее этого хочу?)

Итак, в конце я вижу imageLoaded.current увеличение при загрузке новых изображений, но useLayoutEffect не срабатывает

1 Ответ

0 голосов
/ 31 января 2020

Я не смог решить эту проблему, используя useEffect или аналогичный

Вместо этого я использовал простую функцию для настройки сетки и вызова ее из ленивой загрузки логики c всякий раз, когда он загружает изображение

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} />;
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...