* const [myState, setMyState] = use State(0)
setMyState(1)
useLayoutEffect(() =>{}, [myState])
so when the state changes, I adjust the gridsetMyState(0)
, so that when a new image is loaded it will trigger again the useLayoutEffect
useLayoutEffect
, which again resets the state, creating an infinite loopsetMyState(1)
doesn't change anything and the useLayoutEffect
is not triggeredThis is a simplified code
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} />;
}
I cannot change the state in the useLayoutEffect
, so I will change stuff only when the images are loaded and not again
When I scroll and more images are loaded, I cannot triger the useLayoutEffect anymore
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} />;
}
If I don't want the repaint, but want to trigger the useLayoutEffect, I could use useRef
instead of 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} />;
}
But codesandbox already warns me that imageLoaded.current
will not cause a re-render of the component (even though I more or less wants this?)
So at the end, I see imageLoaded.current
increasing when new images are loaded, but the useLayoutEffect
is not triggered
I haven't been able to solve that using useEffect
or similar
Instead, I've used a simple function to adjust the grid, and call it from the lazy-loading logic whenever it loads an image
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} />;
}