Home > Software engineering >  How to properly splitt dispatch and state so that I dont get re-renders?
How to properly splitt dispatch and state so that I dont get re-renders?

Time:08-17

I had implemented useContext useReducer and I found that I was getting re-renders when only dispatch changed.

I could have two separate components and if one dispatch was triggered both component got changed.

Example:

enter image description here

Both increment and decrement got rendered on each state update.

I found this article that I have followed but I still get the same result.

the code:

export default function App() {
  return (
    <MyContextProvider>
      <Count />
      <ButtonIncrement /> <br /> <br />
      <ButtonDecrement />
    </MyContextProvider>
  );
}

Provider:

import * as React from 'react';
import {
  InitalState,
  ApplicationContextDispatch,
  ApplicationContextState,
} from './Context';
import { applicationReducer } from './Reducer';

export const MyContextProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer(applicationReducer, InitalState);

  return (
    <ApplicationContextDispatch.Provider value={{ dispatch }}>
      <ApplicationContextState.Provider value={{ state }}>
        {children}
      </ApplicationContextState.Provider>
    </ApplicationContextDispatch.Provider>
  );
};

Context:

import React, { Dispatch } from 'react';

export enum ApplicationActions {
  increment = 'increment',
  decrement = 'decrement',
  notification = 'Notification',
}

export type ActionType = ApplicationActions;

export const ActionTypes = { ...ApplicationActions };

export type StateType = {
  count: number;
  notification: string | undefined;
};

export type Action = {
  type: ActionType;
  payload?: string | undefined;
};

interface IContextPropsState {
  state: StateType;
}

interface IContextPropsDispatch {
  dispatch: Dispatch<Action>;
}

export const ApplicationContextState = React.createContext<IContextPropsState>(
  {} as IContextPropsState
);

export const ApplicationContextDispatch =
  React.createContext<IContextPropsDispatch>({} as IContextPropsDispatch);

export const useApplicationContextState = (): IContextPropsState => {
  return React.useContext(ApplicationContextState);
};

export const useApplicationContextDispatch = (): IContextPropsDispatch => {
  return React.useContext(ApplicationContextDispatch);
};

export const InitalState: StateType = {
  count: 0,
  notification: '',
};

Reducer:

import { StateType, Action, ActionTypes } from './Context';

export const applicationReducer = (
  state: StateType,
  action: Action
): StateType => {
  const { type } = action;
  switch (type) {
    case ActionTypes.increment:
      return { ...state, count: state.count   1 };
    case ActionTypes.decrement:
      return { ...state, count: state.count - 1 };
    case ActionTypes.notification:
      return { ...state, notification: action.payload };
    default:
      return state;
  }
};

Working example here

In the article above this fiddle was presented as an example which I based my attempt on but I dont know where Im going wrong.

Note that the original example of this was done without typescript but in my attempt I am adding typescript into the mix.

CodePudding user response:

The problem is that you are passing a new object into your context providers. It's a classic gotcha. Passing objects as props means you are passing a new object every time which will fail prop reference checks.

Pass dispatch, state directly to the providers i.e. value={dispatch}

https://reactjs.org/docs/context.html#caveats

Caveats

Because context uses reference identity to determine when to re-render, there are some gotchas that could trigger unintentional renders in consumers when a provider’s parent re-renders. For example, the code below will re-render all consumers every time the Provider re-renders because a new object is always created for value:

 <MyContext.Provider value={{something: 'something'}}>

CodePudding user response:

@Yuji 'Tomita' Tomita Was 100% on the money.

I had to change my code so that it did not wrap the state and disatch inot an object which in turn made it work as desiered.

Updated code here: https://stackblitz.com/edit/react-ts-7nuhzk?file=Provider.tsx,ButtonDecrement.tsx,App.tsx

  • Related