1
1
'use client' ;
2
2
3
- import React , { ReactNode , useCallback , useState } from 'react' ;
3
+ import React , {
4
+ createContext ,
5
+ ReactNode ,
6
+ useCallback ,
7
+ useContext ,
8
+ useState ,
9
+ } from 'react' ;
4
10
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 ;
9
22
}
10
23
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 > ;
18
33
} ;
19
34
20
35
/**
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}>
23
38
* Client-side component usage example:
24
- * const clientContext = useClientContext();
39
+ * const clientContext = useClientContext<FetchApiContext> ();
25
40
* 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
28
43
*/
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 , > ( {
40
45
children,
41
- value = CLIENT_CTX_VALUE ,
46
+ value,
47
+ defaultValue,
42
48
} : {
43
49
children : ReactNode ;
44
- value ?: Partial < ClientContextData > ;
50
+ value ?: Partial < T > ;
51
+ defaultValue : T ;
45
52
} ) => {
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
+ } ) ;
47
60
48
61
const updateContext = useCallback (
49
- ( newCtxValue : Partial < ClientContextData > ) => {
62
+ ( newCtxValue : Partial < T > ) => {
50
63
setContextValue ( ( prevContextValue ) => ( {
51
64
...prevContextValue ,
52
65
...newCtxValue ,
@@ -58,7 +71,6 @@ export const ClientProvider = ({
58
71
return (
59
72
< ClientContext . Provider
60
73
value = { {
61
- ...CLIENT_CTX_VALUE ,
62
74
...contextValue ,
63
75
updateClientCtx : updateContext ,
64
76
} }
0 commit comments