blob: 3c47ceffea8daf05b75ac348228d92d8d8ca5ee0 [file] [log] [blame]
simon5c677962022-12-02 16:51:54 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import { Context, useCallback, useState } from 'react';
19
20import HookComponent from '../hooks/HookComponent';
21import { isRequired, PartialNotOptional, WithChildren } from '../utils/utils';
22
23export type ConditionalContextProviderProps<T, B extends object> = WithChildren & {
24 Context: Context<T>;
25 initialValue: T;
26
27 /**
28 * Object containing the values used to get the provider value with `useProviderValue`.
29 * If one or more field is undefined, the hook will not be called and `initialValue` will be used for the provider
30 * value.
31 *
32 * Should be wrapped in `useMemo` to avoid unnecessary hook calls.
33 */
34 dependencies: PartialNotOptional<B>;
35 useProviderValue: (dependencies: B) => T;
36};
37
38/**
39 * A context provider with `initialValue` as its value if not all props of the dependencies object are defined.
40 * If all props are defined, the provider value is the result of `useProviderValue`.
41 */
42const ConditionalContextProvider = <T, B extends object>({
43 Context,
44 initialValue,
45 dependencies,
46 useProviderValue,
47 children,
48}: ConditionalContextProviderProps<T, B>) => {
49 const [value, setValue] = useState(initialValue);
50 const unmountCallback = useCallback(() => setValue(initialValue), [initialValue]);
51
52 return (
53 <>
54 {isRequired(dependencies) && (
55 <HookComponent
56 callback={setValue}
57 unmountCallback={unmountCallback}
58 useHook={useProviderValue}
59 args={dependencies}
60 />
61 )}
62 <Context.Provider value={value}>{children}</Context.Provider>
63 </>
64 );
65};
66
67export default ConditionalContextProvider;