The efficiency of React Context API's setters is remarkably sluggish

I have a goal to implement a functionality where the background gradient of a page changes depending on whether the child's sublinks are expanded or collapsed. To achieve this, I am using the useContext hook. However, I've noticed that although everything works as expected, there is a delay of approximately 20 seconds for the background gradient to update when the sublinks are collapsed or expanded. I came across a similar issue on Stack Overflow, but I couldn't fully comprehend the solution provided. The problem seems to be caused by all children of the context provider getting rerendered due to a change in the state within the context.

const Wrapper = styled('div')<{linksExpanded : boolean}>`
  background-image: linear-gradient(180deg, pink 0.2%, #FFFFFF 3%);
  background-color: white;
  ${({theme})=>theme.breakpoints.up('md')} {
    background-image: linear-gradient(180deg, pink 1.5%, #FFFFFF 5.5%);
  }
  ${({linksExpanded , theme})=>linksExpanded && `
    background-image: linear-gradient(180deg, pink 2.5%, #FFFFFF 4.25%);
    ${theme.breakpoints.up('md')} {
      background-image: linear-gradient(180deg, pink 6.5%, #FFFFFF 10%);
    }
  `}
`

const Home = () =>
{
  const { linksExpanded } = useContext(HomeDataContext);
  return (
            <Wrapper linksExpanded={linksExpanded}>
              <About />
              <Links />
              <Contacts />
              <Gallery />
            </Wrapper>
  );
};

export default Home;

export const Landing: FC = () => <PageSkeleton
        pageContent={
          <HomeDataContextProvider><Home /></HomeDataContextProvider>}
      />;


const Links = () => {
  const { currentCakes, linksExpanded, setLinksExpanded } = useContext(HomeDataContext);
  const renderLinks = () => (
    <S.List style={{maxHeight: !linksExpanded ? '0px' : '1000px'}}>
      {currentCakes.map((el: CakeDescription, key : number) => (
        <li key={el.name}>
          <a href='https://google.com'>Link</a>
        </li>
      ))}
    </S.List>
  );
  const handleLinksExpand = () => {
    setLinksExpanded(!linksExpanded);
  };
  return (
    <S.Container>
        <S.ExpandableLinkContainer>
          <ExpandableLink
            onClick={handleLinksExpand}
            open={linksExpanded}
            title='Cakes'
          />
        </S.ExpandableLinkContainer>
        {renderLinks()}
    </S.Container>
  );
};
export default Links;

Now let's take a look at the Context:

export type HomeDataState = {
  currentCakes: Array<CakeDescription>;
  setCurrentCakes?: any;
  linksExpanded: boolean;
  setLinksExpanded?: any;
};

const initialValue: HomeDataState = {
  currentCakes: [],
  linksExpanded: false,
};

export const HomeDataContext = createContext(initialValue as HomeDataState);

export const HomeDataContextProvider: FC = ({ children }) => {
  const [linksExpanded, setLinksExpanded] = useState(initialValue.linksExpanded);
  const [currentCakes, setCurrentCakes] = useState<CakeDescription[]>(initialValue.cakes);
  const fetchCakes = async () => {
    setCurrCakes(
      await myAxiosConstruct.get(process.env.api);
    );
  };
  useEffect(() => {
    fetchCakes();
  }, []);
  
  return (
    <HomeDataContext.Provider
      value={{
        currentCakes,
        linksExpanded,
        setLinksExpanded,
      }}
    >
      {children}
    </HomeDataContext.Provider>
  );
};

Answer №1

Given that the Main element is consuming the MainDataContext, regardless of which property is updated, React will trigger a complete re-rendering of the entire component and all its child components (Header, About, Links, Contact, Gallery). This behavior is intended.

However, we can improve this scenario by dividing the context into two separate contexts: LinksContext and CakesContext. Each context will have its own provider. Then, we can refactor the application as follows:

  1. renderLinks() has been converted into a component called Cakes. This component will only be rendered again if currentCakes is updated. Previously, it was being rendered on every state change.
  2. By wrapping Cakes with memo, we prevent unnecessary re-renders when Main updates.
  3. Similarly, we should use memo for both the Contact and Gallery components to avoid redundant re-renders.

Give these changes a try! Hopefully, they will help optimize your application. For a detailed explanation of optimization techniques and best practices, check out this resource: React Context, All in One

const Wrapper = styled('div')<{linksExpanded : boolean}>`
  background-image: linear-gradient(180deg, pink 0.2%, #FFFFFF 3%);
  background-color: white;
  ${({theme})=>theme.breakpoints.up('md')} {
    background-image: linear-gradient(180deg, pink 1.5%, #FFFFFF 5.5%);
  }
  ${({linksExpanded , theme})=>linksExpanded && `
    background-image: linear-gradient(180deg, pink 2.5%, #FFFFFF 4.25%);
    ${theme.breakpoints.up('md')} {
      background-image: linear-gradient(180deg, pink 6.5%, #FFFFFF 10%);
    }
  `}
`

export const Main = () =>
{
  const { linksExpanded, setLinksExpanded } = useContext(LinksContext);
  return (
            <Wrapper linksExpanded={linksExpanded}>
              <About />
              <Links linksExpanded={linksExpanded} setLinksExpanded={setLinksExpanded} />
              <Contact />
              <Gallery />
            </Wrapper>
  );
};

export const LandingPage = () => <Skeleton
        pageContent={
          <MainDataContextProvider>
            <Main />
          </MainDataContextProvider>
        } />;

export const Cakes = memo(() => {
  const { currentCakes } = useContext(CakesContext)

  return currentCakes.map((el, key) => (
    <li key={el.name}>
      <a href='https://google.com'>Link</a>
    </li>
  ))
})

export const Links = ({ linksExpanded, setLinksExpanded }) => {
  const handleLinksExpand = () => {
    setLinksExpanded(!linksExpanded);
  };

  return (
    <S.Container>
        <S.ExpandableLinkContainer>
          <ExpandableLink
            onClick={handleLinksExpand}
            open={linksExpanded}
            title='Cakes'
          />
        </S.ExpandableLinkContainer>
        <S.List style={{maxHeight: !linksExpanded ? '0px' : '1000px'}}>
          <Cakes/>
        </S.List>
    </S.Container>
  );
};

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

Detecting worldwide route adjustments in Nextjs 13

Is there a way to detect when my route changes in order to trigger a specific event? I want to be able to handle this change and take action accordingly. I am currently facing challenges with Nextjs 13 and its implementation of the app router. As per best ...

The function $(...) does not recognize tablesorter

Currently, I am encountering issues with the tablesorter plugin as my system is unable to recognize the existing function. It is unclear whether there might be a conflict with other JavaScript files, especially since I am implementing changes within a Word ...

Change the boxShadow and background properties of the material-ui Paper component

I am currently referencing their documentation found at this link in order to customize default Paper component properties. Below is the code snippet I have: import { styled } from '@mui/material/styles'; import { Modal, Button, TextField, Grid, ...

"Step-by-step guide on populating a select box with data from the scope

Hey everyone, I'm new to using Angular and hoping for some help with a simple question. I've created a form (simplified version below) that I want users to see a live preview as they fill it out. Everything was going smoothly with regular field ...

Exploring the capabilities of Angular and UIGrid for fetching table information

I have been utilizing Angular along with uigrid, which is an excellent library for displaying data in a tabular format. Everything looks good when displaying the table. However, when I update an item and click on a Save button that triggers a rest service ...

In React JS, the data from my response is not being saved into the variable

My goal is to store the response data in the variable activityType. Within the useEffect function, I am iterating over an API based on the tabs value. The API will return a boolean value of either true or false. While I can successfully log these values ...

Styling a dynamically-injected div using an AJAX request (xhrGet)

Hello everyone, currently I am using the code below to fetch updated content after receiving a "push" event from a server. The new content is then used to replace the existing div/content. However, I'm facing an issue where none of my styles from the ...

"Encountering an issue with Next.js due to npm dependencies importing CSS files

I am working on a next.js project and I am trying to incorporate a component from npm that internally imports typeface-montserrat. However, when trying to do so, Next.js is throwing an error: <path>/node_modules/typeface-montserrate/index.css:2 @fo ...

Struggling to integrate Material UI (MUI) into my React application

I have followed all the necessary steps to install materialUI, emotion/react, and emotion/styled in my react app. However, I am encountering an issue where MUI does not seem to work properly. There are no errors displayed on the console or webpage, but not ...

Reveal the Content-Disposition header in NextJS

I am attempting to retrieve the Content-Disposition header from a response received via axios from an external API. Even though I can see the header in Chrome DevTools Network Response, I am unable to access it from the server. I came across this article ...

Is the jquery autocomeplete plugin malfunctioning when using numbers in the input?

I encountered a requirement to display stock number suggestions within a search box. To achieve this, I decided to implement the Jquery autocomplete plugin. Through an ajax call to a function in my cfc, I was able to retrieve all the stock numbers and stor ...

I encountered an error while trying to deploy my next.js project on Vercel - it seems that the module 'react-icons/Fa' cannot be found, along with

I'm currently in the process of deploying my Next.js TypeScript project on Vercel, but I've encountered an error. Can someone please help me with fixing this bug? Should I try running "npm run build" and then push the changes to GitHub again? Tha ...

Ways to determine if an element is at the top of a page

I need help with a test case that involves checking if the title scrolls up to the top of the page when a button is clicked. The challenge is that there is a default header on the page, so the title should scroll below that. How can we verify this scenar ...

A step-by-step guide on using Javascript to transform images into text

Hey there! I'm looking for some help to convert an image into text. The idea is that when someone uploads an image and hits the "Submit" button, the text from the image should display in a textarea below. However, the code I've been working on do ...

The deployed site is returning a response of "undefined" when attempting to log in through the endpoint

After successfully developing my site using Nextjs, Nodejs, MongoDB, and Express, I encountered an issue when trying to log in on the deployed version. The error message that keeps popping up is . Strangely, it seems to be appending "undefined" to the endp ...

Ways to extract the maximum value from a dataset formatted in JSON

My current project involves using highcharts to create data series for plotting the chart, which looks something like this: series: [{ data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4] }] I have been considering ...

MUI version 5 - Keep styling separate from component files

Seeking to segregate styling from component File in MUI v5. In the past, I accomplished this in v4 by utilizing makeStyles as shown below: Page.style.js: import { makeStyles } from "@material-ui/core"; export const useStyles = makeStyles({ ro ...

Synchronize user accounts across various tabs by utilizing the @auth0/auth0-react library

After successfully implementing the login feature using @auth0/auth0-react in both my React application and website built with Next JS, I encountered an issue. Although I integrated the login feature to sync user information between the app and website, I ...

Revolutionizing Form Select Field: Introducing Rails' Dynamic Input Rendering

I'm a beginner in coding, so please bear with me if my question sounds amateurish. Currently, I am developing an e-commerce website where customers can order posters from images uploaded to the site. They should be able to choose the size of the poste ...

Encountering an error with the iconv-lite package in TypeScript code

I recently added the "iconv-lite" package to my project, imported the module, and attempted to use the decode method. However, I encountered the following error: TypeError: Cannot read properties of undefined (reading 'decode') Interestingly, ...