I am making a background component using Vanta in NextJS, code below:
import { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import FOG from "vanta/dist/vanta.fog.min";
import { useDarkMode, useWindowSize } from "./Hooks";
const updateColor = (darkMode) => {
return {
highlightColor: darkMode ? 0xff0000 : 0xffd2c6,
midtoneColor: darkMode ? 0xbe1d1d : 0xff7737,
lowlightColor: darkMode ? 0x781212 : 0x69c3ff,
baseColor: darkMode ? 0x0 : 0xffffff,
}
}
export default function Background({ children }) {
const [vantaEffect, setVantaEffect] = useState();
//returns the width and height of the window
const windowSize = useWindowSize();
//user presses a button to change the theme from light to dark
const [mode] = useDarkMode();
const vantaRef = useRef();
useEffect(() => {
const isDark = mode === "dark";
if (vantaEffect) {
setVantaEffect(
vantaEffect.setOptions({
minHeight: windowSize.height,
minWidth: windowSize.width,
...updateColor(isDark)
}));
vantaEffect.resize();
} else {
setVantaEffect(
FOG({
THREE,
el: vantaRef.current,
blurFactor: 0.18,
minHeight: window.innerHeight,
minWidth: window.innerWidth,
...updateColor(isDark)
})
)
}
return () => {
if (vantaEffect) vantaEffect.destroy();
}
}, [mode, windowSize]);
return (
<div>
<div className="fixed -z-50" ref={vantaRef}></div>
<div>{children}</div>
</div>
)
}
Right now, if either mode or windowSize is updated, the canvas will both be resized, and re-rendered for dark mode. I want to be able to detect whether it was dark mode or the window being resized ran the useEffect function.
Additionally, whenever I try to lint the function, it asks me to add vantaEffect to dependencies. When I do, the effect renders too many times and lags the page.
Is there a better way to use hooks to render the background?
CodePudding user response:
To split up the mode
and windowResize
you should use two separate useEffect
hooks. By splitting them up these hooks will only execute when the dependency is updated.
What I noticed in your code is that you execute vantaEffect.destroy()
on every render. I might be mistaken, but I think that is not what you want.
If you want to execute this code only on unmount
you should add another useEffect
without any dependencies in its array. (as stated in the React docs:
When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time.
https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1)
The total would look something like this:
useEffect(() => {
const isDark = mode === "dark";
if (vantaEffect) {
setVantaEffect(
vantaEffect.setOptions({
...updateColor(isDark)
}));
vantaEffect.resize();
} else {
setVantaEffect(
FOG({
THREE,
el: vantaRef.current,
blurFactor: 0.18,
minHeight: window.innerHeight,
minWidth: window.innerWidth,
...updateColor(isDark)
})
)
}
}, [mode]);
useEffect(() => {
if (vantaEffect) {
setVantaEffect(
vantaEffect.setOptions({
minHeight: windowSize.height,
minWidth: windowSize.width
}));
vantaEffect.resize();
} else {
setVantaEffect(
FOG({
THREE,
el: vantaRef.current,
blurFactor: 0.18,
minHeight: window.innerHeight,
minWidth: window.innerWidth
})
)
}
}, [windowSize]);
useEffect(() => {
// only destroying the vantaEffect on unmount
return () => vantaEffect?.destroy();
}, []);
You should optimize the output, but I hope you get the idea
CodePudding user response:
I want to be able to detect whether it was dark mode or the window being resized ran the useEffect function.
You can have two effects in your component one for mode
and the other for windowSize
.
Additionally, whenever I try to lint the function, it asks me to add vantaEffect to dependencies. When I do, the effect renders too many times and lags the page.
You can use functional state update to access the previous state:
useEffect(() => {
const isDark = mode === "dark";
setVantaEffect((previousVantaEffect) => {
// use `previousVantaEffect` and return what you want
})
}, [mode])