Home > front end >  Create contexts in multiple files and share functions and states with each other
Create contexts in multiple files and share functions and states with each other

Time:02-27

I've got two contexts each in their own file, but now I need a function from one file to invoke a function from the other one. The logout() in UserContext needs to invoke the resetOrder() from the OrderContext.

globalState.tsx:

import React from 'react';
import {UserProvider} from './user';
import {OrderProvider} from './order';

function ProviderComposer({contexts, children}: any) {
  return contexts.reduceRight((kids: any, parent: any) => React.cloneElement(parent, {children: kids}), children);
}

function ContextProvider({children}: any) {
  return <ProviderComposer contexts={[<UserProvider />, <OrderProvider />]}>{children}</ProviderComposer>;
}

export {ContextProvider};

index.ts:

import {UserContext, UserProvider} from './user';
import {OrderContext, OrderProvider} from './order';

export {UserContext, UserProvider, OrderContext, OrderProvider};

order.tsx

import React, {createContext, useRef, useState} from 'react';

// context
export const OrderContext = createContext<any>('');

// interfaces
import {IproductsStatus, Iorder} from 'src/types/Orders';

// utils
import {ORDER_UPDATE_METHOD, blankOrder, ORDER, HAS_SUBMITTED_ORDER, getLocal, copyByValue} from 'src/utils';

export function OrderProvider(props: any) {
  // variables
  const isOrderMounted = useRef<boolean>(false);
  const isProductUpdating = useRef<boolean>();
  const selectedUpdateMethod = useRef<number>(ORDER_UPDATE_METHOD.none);
  const productsStatus = useRef<IproductsStatus>({});
  const [order, setOrder] = useState<Iorder>(getLocal(ORDER) !== null ? getLocal(ORDER) : copyByValue(blankOrder));
  const [hasOrderBeenSubmitted, setHasOrderBeenSubmitted] = useState(
    getLocal(HAS_SUBMITTED_ORDER) !== null ? true : false,
  );

  function resetOrder() {
    localStorage.removeItem(ORDER);
    productsStatus.current = {};
    isOrderMounted.current = false;
    isProductUpdating.current = false;
    selectedUpdateMethod.current = ORDER_UPDATE_METHOD.none;
    if (getLocal(HAS_SUBMITTED_ORDER) !== null) localStorage.removeItem(HAS_SUBMITTED_ORDER);
    setOrder(copyByValue(blankOrder));
    setHasOrderBeenSubmitted(false);
  }

  return (
    <OrderContext.Provider
      value={{
        isProductUpdating,
        productsStatus,
        selectedUpdateMethod,
        order,
        hasOrderBeenSubmitted,
        setHasOrderBeenSubmitted,
        resetOrder,
      }}>
      {props.children}
    </OrderContext.Provider>
  );
}

user.tsx:

import React, {createContext, useState} from 'react';

// utils
import {getLocal, AUTH_TOKEN, USER_INFO} from 'src/utils/index';

// context
export const UserContext = createContext<any>('');

export function UserProvider(props: any) {
  const [isLoggedIn, setIsLoggedIn] = useState(
    !!getLocal(AUTH_TOKEN) &&
      !!getLocal(USER_INFO) &&
      !!getLocal(USER_INFO).profile &&
      getLocal(USER_INFO).profile.id > 0,
  );
  const [user, setUser] = useState(
    !!getLocal(AUTH_TOKEN) &&
      !!getLocal(USER_INFO) &&
      !!getLocal(USER_INFO).profile &&
      getLocal(USER_INFO).profile.id > 0
      ? getLocal(USER_INFO)
      : {profile: {id: 0}, paymentOptions: []},
  );

  const logout = () => {
    setIsLoggedIn(false);
    localStorage.clear();
    sessionStorage.clear();
    // resetOrders()
    setUser({profile: {id: 0}, paymentOptions: []});
  };

  return (
    <UserContext.Provider
      value={{
        isLoggedIn,
        logout,
        user,
        setUser,
      }}>
      {props.children}
    </UserContext.Provider>
  );
}

If I could figure out how to share the functions/variables between the two, I'd like to separate them into even more context files. But the only thing I can think of sadly is to combine them and create one enormous file. (which would make me want to go back to using a state management plugin such as redux).

CodePudding user response:

You can simply put them one inside another in this order, and you can use everything from OrderContext inside UserContext:

import React from 'react';
import {UserProvider} from './user';
import {OrderProvider} from './order';


function ContextProvider({children}: any) {
  return (
           <OrderProvider>
             <UserProvider>{children}</UserProvider>
           </OrderProvider>
         )
}

export {ContextProvider};

And you can use it inside user.tsx like this, using useContext from React:

import React, {createContext, useState, useContext} from 'react';

// utils
import {getLocal, AUTH_TOKEN, USER_INFO} from 'src/utils/index';

// context
export const UserContext = createContext<any>('');

// import OrderContext
import {OrderContext} from "order.tsx"

export function UserProvider(props: any) {
  const [isLoggedIn, setIsLoggedIn] = useState(
    !!getLocal(AUTH_TOKEN) &&
      !!getLocal(USER_INFO) &&
      !!getLocal(USER_INFO).profile &&
      getLocal(USER_INFO).profile.id > 0,
  );
  const [user, setUser] = useState(
    !!getLocal(AUTH_TOKEN) &&
      !!getLocal(USER_INFO) &&
      !!getLocal(USER_INFO).profile &&
      getLocal(USER_INFO).profile.id > 0
      ? getLocal(USER_INFO)
      : {profile: {id: 0}, paymentOptions: []},
  );
  // grab resetOrder from OrderContext
  const {resetOrder}= useContext(OrderContext);

  const logout = () => {
    setIsLoggedIn(false);
    localStorage.clear();
    sessionStorage.clear();
    resetOrders()
    setUser({profile: {id: 0}, paymentOptions: []});
  };

  return (
    <UserContext.Provider
      value={{
        isLoggedIn,
        logout,
        user,
        setUser,
      }}>
      {props.children}
    </UserContext.Provider>
  );
}

CodePudding user response:

You can rearrange the elements in the context providers array so that UserProvider gets wrapped by OrderProvider:

function ContextProvider({children}: any) {
  return <ProviderComposer contexts={[<OrderProvider />, <UserProvider />]}>{children}</ProviderComposer>;
}

you can now access resetOrders inside UserProvider using useContext:

const { resetOrders } = useContext(OrderContext)
  • Related