11'use client' ;
22
3- import React , { ReactNode , useCallback , useState } from 'react' ;
3+ import React , {
4+ createContext ,
5+ ReactNode ,
6+ useCallback ,
7+ useContext ,
8+ useState ,
9+ } from 'react' ;
410
5- export interface ClientContextData {
6- topError : ReactNode ;
7- fetchCount : number ;
8- updateClientCtx : ( props : Partial < ClientContextData > ) => void ;
11+ /**
12+ * This is a generic custom hook for updating the client context
13+ * It can be used in multiple places from any client-side component
14+ * Please change the per-defined type & default value in constants/context.ts
15+ */
16+
17+ export const OUTSIDE_CLIENT_PROVIDER_ERROR =
18+ 'Cannot be used outside ClientProvider!' ;
19+
20+ export interface UpdateClientCtxType < T > {
21+ updateClientCtx : ( props : Partial < T > ) => void ;
922}
1023
11- const CLIENT_CTX_VALUE : ClientContextData = {
12- topError : null ,
13- fetchCount : 0 ,
14- updateClientCtx : ( ) => {
15- // console.error('Cannot be used outside ClientProvider');
16- throw new Error ( 'Cannot be used outside ClientProvider' ) ;
17- } ,
24+ export const ClientContext = createContext < unknown | undefined > ( undefined ) ;
25+
26+ export const useClientContext = < T , > ( ) : T & UpdateClientCtxType < T > => {
27+ const context = useContext ( ClientContext ) ;
28+ if ( context === undefined ) {
29+ throw new Error ( OUTSIDE_CLIENT_PROVIDER_ERROR ) ;
30+ }
31+
32+ return context as T & UpdateClientCtxType < T > ;
1833} ;
1934
2035/**
21- * You should change the above interface and default value as per your requirement
22- * No need to change the below code
36+ * You should pass the default value to the ClientProvider first
37+ * e.g. <ClientProvider defaultValue={FETCH_API_CTX_VALUE} value={dynamicValue}>
2338 * Client-side component usage example:
24- * const clientContext = useClientContext();
39+ * const clientContext = useClientContext<FetchApiContext> ();
2540 * clientContext.updateClientCtx({ topError: 'Error message' });
26- * clientContext.updateClientCtx({ totalRenderCount : 10 });
27- * The total render count is: clientContext.totalRenderCount
41+ * clientContext.updateClientCtx({ fetchCount : 10 });
42+ * The total fetch count is: clientContext.fetchCount
2843 */
29- export const ClientContext =
30- React . createContext < ClientContextData > ( CLIENT_CTX_VALUE ) ;
31-
32- export const useClientContext = ( ) : ClientContextData => {
33- const context = React . useContext ( ClientContext ) ;
34- if ( ! context ) throw new Error ( 'Cannot be used outside ClientProvider' ) ;
35-
36- return context ;
37- } ;
38-
39- export const ClientProvider = ( {
44+ export const ClientProvider = < T , > ( {
4045 children,
41- value = CLIENT_CTX_VALUE ,
46+ value,
47+ defaultValue,
4248} : {
4349 children : ReactNode ;
44- value ?: Partial < ClientContextData > ;
50+ value ?: Partial < T > ;
51+ defaultValue : T ;
4552} ) => {
46- const [ contextValue , setContextValue ] = useState ( value ) ;
53+ const [ contextValue , setContextValue ] = useState ( {
54+ ...defaultValue ,
55+ ...value ,
56+ updateClientCtx : ( _ : Partial < T > ) : void => {
57+ throw new Error ( OUTSIDE_CLIENT_PROVIDER_ERROR ) ;
58+ } ,
59+ } ) ;
4760
4861 const updateContext = useCallback (
49- ( newCtxValue : Partial < ClientContextData > ) => {
62+ ( newCtxValue : Partial < T > ) => {
5063 setContextValue ( ( prevContextValue ) => ( {
5164 ...prevContextValue ,
5265 ...newCtxValue ,
@@ -58,7 +71,6 @@ export const ClientProvider = ({
5871 return (
5972 < ClientContext . Provider
6073 value = { {
61- ...CLIENT_CTX_VALUE ,
6274 ...contextValue ,
6375 updateClientCtx : updateContext ,
6476 } }
0 commit comments