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)