A guide on updating various states using React Hooks

Creating a background component with the use of Vanta in NextJS, here's the code snippet:

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();
    //function to get window dimensions
    const windowSize = useWindowSize();
    //toggle between light and dark mode
    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>
    )
}

Currently, whenever mode or windowSize changes, both canvas resizing and re-rendering for dark mode occur. Is there a way to distinguish whether it was triggered by dark mode or window resize?

Furthermore, when I try to lint the function, it suggests adding vantaEffect to dependencies. Doing so causes the effect to render excessively and slow down the page. Any suggestions on optimizing the usage of hooks for rendering the background?

Answer №1

If you want to separate the functionality for mode and windowResize, it's best to use two distinct useEffect hooks. This ensures that these hooks will only run when their designated dependency is updated.

Upon reviewing your code, I noticed that you are calling vantaEffect.destroy() on every render cycle. This may not be the intended behavior. If you only want this action to occur on component unmount, you should include another useEffect with an empty dependency array, as recommended in the React documentation:

When does React clean up effects? React cleans up effects when the component unmounts. Effects run on each render and not just once. Hence, React also cleans up effects from the previous render before executing the next set of effects.

Refer to this link for more information.

The updated approach would resemble the following structure:

    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(() => {
        // execute vantaEffect.destroy() only during unmount
        return () => vantaEffect?.destroy();
    }, []);

Further optimizations can be made, but hopefully, this conveys the concept effectively.

Answer №2

I'm looking to determine whether dark mode is enabled or if the window has been resized before running the useEffect function.

To achieve this, you can separate your effects in your component—one for mode and another for windowSize.

Furthermore, when I attempt to lint the function, it suggests adding vantaEffect to the dependencies. However, upon doing so, the effect causes excessive rendering and page lag.

You can utilize functional state update to access the previous state:

useEffect(() => {
    const isDark = mode === "dark";

    setVantaEffect((previousVantaEffect) => {
       // use `previousVantaEffect` here and return desired output
    })

}, [mode])

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring ways to retrieve nested values from JSON data using the Instagram API and Javascript

Trying to modify the script found at https://github.com/bigflannel/bigflannel-Instafeed in order to access Instagram photos on a website. Unfortunately, the script does not currently support displaying photo comments. I attempted to make modifications that ...

When I run `npm start`, there is no response and I keep getting a "connection

After attempting multiple tutorials, I am facing the same issue where 'npm start' does not execute anything and there are no error messages. Additionally, when I try to open localhost:3000, the connection is refused. Interestingly, switching to ...

Reset the jQuery star-rating plugin

I came across a useful plugin for star ratings called JQuery Star Rating by Daniel Upshaw. My question is, how can I reset the stars in a form using this plugin? $("#btn-reset").on('click', function() { //resetting other inputs $('#st ...

Are there any alternatives to ui-ace specifically designed for Angular 2?

I am currently working on an Angular2 project and I'm looking to display my JSON data in an editor. Previously, while working with AngularJS, I was able to achieve this using ui-ace. Here is an example of how I did it: <textarea ui-ace="{ us ...

Unchecking an available item observable in Knockout.js when clicking on a selected item

When you click on the elements in the top list, a selection is made. If you click on the elements in the bottom list, it will be removed from the list. However, the checkbox in the top list is not being unchecked. How can this issue be resolved? functio ...

Troubleshooting Next JS and Redux: Fix for getInitialProps not returning props

Looking for a skilled react developer to lend a hand. Much appreciated! I'm currently navigating the balance of using redux and NEXT JS effectively without overcomplicating the setup. OBJECTIVE: Utilize getInitialProps(for SSR) to dispatch actions ...

What is the best way to transfer information from df ~ to my webpage?

I'm currently working on a pie chart that visualizes the disk space usage on my Linux machine. I need help figuring out how to properly parse this data onto a microservice URL. Any assistance would be greatly appreciated. Here's what I have so f ...

Steps to Hide a Material-UI FilledInput

Trying to format a FilledInput Material-ui component to show currency using the following package: https://www.npmjs.com/package/react-currency-format Various attempts have been made, but none seem to be successful. A codesandbox showcasing the issue has ...

Create a time of 00:19:59 using JavaScript

I am attempting to display a countdown timer that starts at 20 minutes in the format (00:20:00) using setInterval. Once the countdown is finished, it should display as (00:00:00), but I am having trouble achieving this. <body onload = "onloadFunc();" ...

How do you vertically span a grid element across 3 rows using Material UI?

This particular scenario may appear to be straightforward, but the official documentation of Material UI lacks a clear example on how to achieve this. Even after attempting to nest the grid elements, I encountered an issue where the grid element on the ri ...

Is it possible to set the default checkbox selection in the MUI DataGrid?

Adding a checkbox selection to a MUI DataGrid is made simple with the checkboxSelection attribute and monitoring selection changes with onSelectionChange: <DataGrid columns={columns} rows={rows} pageSize={10} checkboxSelection onSelectionChang ...

NodeJS: Speed up your workflow by compressing video files

In my quest to enhance the performance of my application, I am seeking ways to compress images and videos to their smallest possible size without sacrificing quality. This process is known as lossless compression. While the imagemin package has proven eff ...

React on the server side renders HTML content

Can you explain how to set up a node server in order to host React components/templates and generate HTML as a string by passing props? The idea: const React = require('react'); const { renderToStaticMarkup } = require('react-dom/server&ap ...

Methods to validate CSS attributes specified within a class using React testing library

I am struggling to validate the CSS properties defined within a class in CSS using the React Testing Library. Unfortunately, I am facing challenges in doing so. Here are the simplified code snippets: import React from "react"; import { render, ...

"Send the selected radio button options chosen by the user, with the values specified in a JSON format

My current task involves inserting radio button values into a MySql database using Angular. The form consists of radio buttons with predefined values stored in a json file. Below is an example of how the json file is structured: //data.json [{ "surve ...

The value within the style.setProperty function does not get applied

I have a progress bar that dynamically increases based on user input: <div class="progressBarContainer percentBar"> <div class="progressBarPercent" style="--width:${gPercent}" id="${gName}-pbar">< ...

What is the best way to remove an element from an array and add a new one?

Here is the array that I am working with: [ { "id": "z12", "val": "lu", "val2": "1", }, { "id": "z13", "val": "la", "val2" ...

Ensure that the input remains below ten

My goal here is to ensure that the value in an input element is a non-zero digit (0<x<=9). Here's the HTML tag I'm using: <input type="number" class="cell"> I've experimented with various JavaScript solutions, but so far none h ...

Tips for transmitting an array of Objects to AngularJS

Summary I'm curious about how I can successfully send the following array of objects from NodeJS to an AngularJS app. var players = [ Footer: { username: 'Footer', hp: 200, exp: 1 }, Barter: { username: 'Barter', hp: 200, e ...

Setting up the current user's location when loading a map with Angular-google-maps

I am currently utilizing the library in conjunction with the IONIC framework. To manually set the center of the map, I have implemented the following code snippet: .controller('mainCtrl', function($scope) { $scope.map = { cen ...