-
Notifications
You must be signed in to change notification settings - Fork 559
/
UserProvider.tsx
93 lines (81 loc) · 3.05 KB
/
UserProvider.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2022 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
import React, { createContext, useContext, useEffect, useState } from "react";
import { useApp } from "./AppProvider";
/**
* Create a context containing the Realm app. Should be accessed with the useApp hook.
*/
export const UserContext = createContext<Realm.User | null>(null);
type UserProviderProps = {
/**
* The fallback component to render if there is no authorized user. This can be used
* to render a login screen or another component which will log the user in.
*/
fallback?: React.ComponentType<unknown> | React.ReactElement | null | undefined;
children: React.ReactNode;
};
/**
* React component providing a Realm user on the context for the sync hooks
* to use. A `UserProvider` is required for an app to use the hooks.
*/
export const UserProvider: React.FC<UserProviderProps> = ({ fallback: Fallback, children }) => {
const app = useApp();
const [user, setUser] = useState<Realm.User | null>(() => app.currentUser);
// Support for a possible change in configuration
if (app.currentUser?.id != user?.id) {
setUser(app.currentUser);
}
useEffect(() => {
const event = () => {
if (app.currentUser?.id != user?.id) {
setUser(app.currentUser);
}
};
user?.addListener(event);
app?.addListener(event);
return () => {
user?.removeListener(event);
app?.removeListener(event);
};
}, [user, app]);
if (!user) {
if (typeof Fallback === "function") {
return <Fallback />;
}
return <>{Fallback}</>;
}
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
};
/**
* Hook to access the currently authenticated Realm user from the
* {@link UserProvider} context. The user is stored as React state,
* so will trigger a re-render whenever it changes (e.g. logging in,
* logging out, switching user).
*
*/
export const useUser = <
FunctionsFactoryType extends Realm.DefaultFunctionsFactory,
CustomDataType extends Record<string, unknown>,
UserProfileDataType extends Realm.DefaultUserProfileData,
>(): Realm.User<FunctionsFactoryType, CustomDataType, UserProfileDataType> => {
const user = useContext(UserContext);
if (!user) {
throw new Error("No user found. Did you forget to wrap your component in a <UserProvider>?");
}
return user as Realm.User<FunctionsFactoryType, CustomDataType, UserProfileDataType>;
};