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+ export interface UpdateClientCtxType < T > {
20+ updateClientCtx : ( props : Partial < T > ) => void ;
921}
1022
11- const CLIENT_CTX_VALUE : ClientContextData = {
12- topError : null ,
13- fetchCount : 0 ,
23+ const UPDATE_CLIENT_CTX = {
1424 updateClientCtx : ( ) => {
15- // console.error('Cannot be used outside ClientProvider');
16- throw new Error ( 'Cannot be used outside ClientProvider' ) ;
25+ throw new Error ( OUTSIDE_CLIENT_PROVIDER_ERROR ) ;
1726 } ,
1827} ;
1928
20- /**
21- * You should change the above interface and default value as per your requirement
22- * No need to change the below code
23- * Client-side component usage example:
24- * const clientContext = useClientContext();
25- * clientContext.updateClientCtx({ topError: 'Error message' });
26- * clientContext.updateClientCtx({ totalRenderCount: 10 });
27- * The total render count is: clientContext.totalRenderCount
28- */
29- export const ClientContext =
30- React . createContext < ClientContextData > ( CLIENT_CTX_VALUE ) ;
29+ export const ClientContext = createContext < unknown | undefined > ( undefined ) ;
3130
32- export const useClientContext = ( ) : ClientContextData => {
33- const context = React . useContext ( ClientContext ) ;
34- if ( ! context ) throw new Error ( 'Cannot be used outside ClientProvider' ) ;
31+ export const useClientContext = < T , > ( ) : T & UpdateClientCtxType < T > => {
32+ const context = useContext ( ClientContext ) ;
33+ if ( context === undefined ) {
34+ throw new Error ( OUTSIDE_CLIENT_PROVIDER_ERROR ) ;
35+ }
3536
36- return context ;
37+ return context as T & UpdateClientCtxType < T > ;
3738} ;
3839
39- export const ClientProvider = ( {
40+ /**
41+ * You should pass the default value to the ClientProvider first
42+ * e.g. <ClientProvider defaultValue={FETCH_API_CTX_VALUE} value={dynamicValue}>
43+ * Client-side component usage example:
44+ * const clientContext = useClientContext<FetchApiContext>();
45+ * clientContext.updateClientCtx({ topError: 'Error message' });
46+ * clientContext.updateClientCtx({ fetchCount: 10 });
47+ * The total fetch count is: clientContext.fetchCount
48+ */
49+ export const ClientProvider = < T , > ( {
4050 children,
41- value = CLIENT_CTX_VALUE ,
51+ value,
52+ defaultValue,
4253} : {
4354 children : ReactNode ;
44- value ?: Partial < ClientContextData > ;
55+ value ?: Partial < T > ;
56+ defaultValue : T ;
4557} ) => {
46- const [ contextValue , setContextValue ] = useState ( value ) ;
58+ const [ contextValue , setContextValue ] = useState ( {
59+ ...defaultValue ,
60+ ...value ,
61+ ...UPDATE_CLIENT_CTX ,
62+ } ) ;
4763
4864 const updateContext = useCallback (
49- ( newCtxValue : Partial < ClientContextData > ) => {
65+ ( newCtxValue : Partial < T > ) => {
5066 setContextValue ( ( prevContextValue ) => ( {
5167 ...prevContextValue ,
5268 ...newCtxValue ,
@@ -58,7 +74,6 @@ export const ClientProvider = ({
5874 return (
5975 < ClientContext . Provider
6076 value = { {
61- ...CLIENT_CTX_VALUE ,
6277 ...contextValue ,
6378 updateClientCtx : updateContext ,
6479 } }
0 commit comments