Reputation: 1068
I am attempting to implement the React useContext hook to share the state of a user license. I've found this pattern online but can't seem to get it to work. If the license is not valid, the user is redirected to a warning page with instructions.
I have these files;
license-context.tsx
import { createContext, useContext, useState, useEffect } from "react";
interface Props {
children: React.ReactNode;
}
export interface LicenseContextModel {
product: boolean | null;
api: string | null;
isLoading: boolean;
}
export const LicenseContext = createContext<LicenseContextModel>({} as LicenseContextModel);
const LicenseContextProvider = ({ children }: Props): JSX.Element => {
const [product, setProduct] = useState(false);
const [api, setApi] = useState('');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const checkLicense = async () => {
try {
console.log('LicenseContextProvider is running');
setProduct(true);
setApi('blah');
} catch (error) {
console.error('License check failed:', error);
}
setIsLoading(false);
};
checkLicense();
},[]);
const licenseContext: LicenseContextModel = {
product,
api,
isLoading,
};
console.log('licenseContext', licenseContext);
return <LicenseContext.Provider value={licenseContext}>{children}</LicenseContext.Provider>;
};
export function useLicense(): LicenseContextModel {
const context = useContext(LicenseContext);
console.log('context', context);
if (!context) {
throw new Error('useLicense must be used within a LicenseContextProvider');
}
return context;
}
export default LicenseContextProvider;
LandingPage.tsx
import React, { useEffect } from "react";
import LicenseContextProvider, { useLicense } from '@/contexts/license-context';
import { useRouter } from 'next/navigation';
const LandingPage = () => {
const { product, api, isLoading } = useLicense();
const router = useRouter();
useEffect(() => {
console.log('page isLoading', isLoading);
if (!isLoading) {
console.log('product', product);
console.log('api', api);
if (!product) {
router.push('/license-warning');
} else if (api === '') {
router.push('/key-manager');
}
}
},[product, api, isLoading]);
return (
<>
<title>Landing Page</title>
<LicenseContextProvider>
<div className="w-full">
<div className="flex flex-col w-full">
<p>Landed on the page</p>
</div>
</div>
</LicenseContextProvider>
</>
);
};
export default LandingPage;
LandingPage imports LicenseContextProvider and useLicense from license-context. The values for product, api, and isLoading are retrieved from the license context using the useLicense() call which uses LicenseContext as the initial context.
The useEffect in LandingPage listens for changes to product, api, and isLoading.
When the component LicenseContextProvider is loaded, it's useEffect sets the product and api values to true and 'blah' respectively and sets isListening to false. However, the useEffect in LandingPage is never triggered by any of the changes and so the call is always routed to the license-warning page.
It seems that there is no connection between LicenseContext and LicenseContextProvider. I'm trying to figure out how to share the state between them.
The console log from the browser shows this;
context Object { } license-context.tsx:57:13
licenseContext license-context.tsx:42:12
Object { product: false, api: "", isLoading: true }
LicenseContextProvider is running license-context.tsx:25:33
page isLoading undefined LandingPage.tsx:22:21
product undefined LandingPage.tsx:24:25
api undefined LandingPage.tsx:25:25
licenseContext license-context.tsx:42:12
Object { product: true, api: "blah", isLoading: false }
I would expect to see another 'page isLoading' update after the licenseContext useEffect finishes but the useEffect in LandingPage is not triggered.
Any suggests are appreciated. I feel like I am missing a basic link between the LicenseContext (which is always undefined) and the LicenseContextProvider (which is updating the product, api, and isLoading states).
Upvotes: 0
Views: 34
Reputation: 1068
Like so many times before, as soon as I post my question I take a break, come back, and give it one more shot.
Changing LandingPage.tsx to this made it all work;
import React, { useEffect } from "react";
import LicenseContextProvider, { useLicense } from '@/contexts/license-context';
import { useRouter } from 'next/navigation';
const InnerLandingPage = () => {
const { product, api, isLoading } = useLicense();
const router = useRouter();
useEffect(() => {
console.log('page isLoading', isLoading);
if (!isLoading) {
console.log('product', product);
console.log('api', api);
if (!product) {
router.push('/license-warning');
} else if (api === '') {
router.push('/key-manager');
}
}
},[product, api, isLoading]);
return (
<>
<p>Inner Landing Page</p>
</>
);
};
const LandingPage = () => {
return (
<>
<title>Landing Page</title>
<LicenseContextProvider>
<div className="w-full">
<div className="flex flex-col w-full">
<InnerLandingPage/>
</div>
</div>
</LicenseContextProvider>
</>
);
};
export default LandingPage;
Upvotes: 0