Skip to content

Commit 20fb4f5

Browse files
authored
Merge pull request #31 from sw-yx/v0.2.1
v0.2.1
2 parents feb70e8 + 8e30593 commit 20fb4f5

File tree

4 files changed

+103
-21
lines changed

4 files changed

+103
-21
lines changed

README.md

+87-7
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ Three demos:
1212
- [example with Reach Router](https://github.com/sw-yx/react-netlify-identity/tree/master/examples/example-reach-router) with [a demo hosted here](https://react-netlify-identity.netlify.com) and [deployed logs here](https://app.netlify.com/sites/react-netlify-identity/deploys)
1313
- [example with React Router](https://github.com/sw-yx/react-netlify-identity/tree/master/examples/example-react-router) with [a demo hosted here](https://react-netlify-identity-example.netlify.com)
1414

15-
1615
**This library is not officially maintained by Netlify.** This is written by swyx for his own use (and others with like minds 😎) and will be maintained as a personal project unless formally adopted by Netlify. See below for official alternatives.
1716

1817
## Blogposts
1918

2019
- [Add Netlify Identity Authentication to any React App in 5 minutes with React Context, Hooks and Suspense](https://dev.to/swyx/add-netlify-identity-authentication-to-any-react-app-in-5-minutes-with-react-context-hooks-and-suspense-5gci)
2120

22-
2321
## List of Alternatives
2422

2523
**Lowest level JS Library**: If you want to use the official Javascript bindings to GoTrue, Netlify's underlying Identity service written in Go, use https://github.com/netlify/gotrue-js
@@ -51,13 +49,19 @@ yarn add react-netlify-identity
5149
- `isConfirmedUser: boolean`: if they have confirmed their email
5250
- `isLoggedIn: boolean`: if the user is logged in
5351
- `signupUser(email: string, password: string, data: Object)`
54-
- `loginUser(email: string, password: string, remember: Boolean)` - we default the `remember` term to `true` since you'll usually want to remember the session in localStorage. set it to false if you need to
52+
- `loginUser(email: string, password: string, remember: boolean = true)` - we default the `remember` term to `true` since you'll usually want to remember the session in localStorage. set it to false if you need to
5553
- `logoutUser()`
5654
- `requestPasswordRecovery(email: string)`
57-
- `recoverAccount(token: string, remember?: boolean | undefined)`
5855
- `updateUser(fields: { data: object })`
5956
- `getFreshJWT()`
60-
- `authedFetch(endpoint: string, obj = {})` (a thin axios-like wrapper over `fetch` that has the user's JWT attached, for convenience pinging Netlify Functions with Netlify Identity)
57+
- `authedFetch(endpoint: string, obj: RequestInit = {})` a thin axios-like wrapper over `fetch` that has the user's JWT attached, for convenience pinging Netlify Functions with Netlify Identity
58+
- `param: TokenParam`
59+
- a token exposing Netlify tokens a dev has to implement the actions for; namely `invite`, `recovery`, `email_change` and `access_denied`
60+
- **Important:** tokens this package exposes no methods for are automatically handled and will not be passed down - see [runRoutes implementation](https://github.com/sw-yx/react-netlify-identity/master/src/runRoutes.tsx)
61+
- if you don't want this behaviour (added [here](https://github.com/sw-yx/react-netlify-identity/issues/12) in v.0.1.8), pass `runRoutes={false}` to the exposed hook
62+
- for further reference, please check the [type definition](https://github.com/sw-yx/react-netlify-identity/tree/master/src/token.ts)
63+
- an example implementation for a Recovery process can be found below
64+
- `recoverAccount(remember?: boolean)`: verifies and consumes the recovery token caught by `runRoutes`, sets user on success
6165

6266
```tsx
6367
import React from 'react';
@@ -89,16 +93,19 @@ function Login() {
8993
const { loginUser, signupUser } = useIdentityContext();
9094
const formRef = React.useRef();
9195
const [msg, setMsg] = React.useState('');
96+
9297
const signup = () => {
9398
const email = formRef.current.email.value;
9499
const password = formRef.current.password.value;
100+
95101
signupUser(email, password)
96102
.then(user => {
97103
console.log('Success! Signed up', user);
98104
navigate('/dashboard');
99105
})
100106
.catch(err => console.error(err) || setMsg('Error: ' + err.message));
101107
};
108+
102109
return (
103110
<form
104111
ref={formRef}
@@ -189,6 +196,81 @@ function Dashboard() {
189196

190197
</details>
191198

199+
<details>
200+
<summary>
201+
<h3>
202+
How to handle a Recovery Action (since v0.2)
203+
</h3>
204+
</summary>
205+
206+
Of course you can alternatively inline this logic into app.
207+
208+
```tsx
209+
import { useIdentityContext } from 'react-netlify-identity';
210+
import {
211+
BrowserRouter as Router,
212+
Switch,
213+
Route,
214+
useLocation,
215+
useHistory,
216+
} from 'react-router-dom';
217+
218+
export default function App() {
219+
const { isLoggedIn } = useIdentityContext();
220+
221+
return (
222+
<Router>
223+
<CatchNetlifyRecoveryNullComponent />
224+
<Switch>
225+
{isLoggedIn ? (
226+
<>
227+
<Route path="/dashboard" exact component={DashboardPage} />
228+
<Route component={() => <Redirect to="/dashbard" />} />
229+
</>
230+
) : (
231+
<>
232+
<Route path="/" exact component={LandingPage} />
233+
<Route path="/register" exact component={RegisterPage} />
234+
<Route path="/login" exact component={LoginPage} />
235+
{/* etc */}
236+
<Route path="/recovery" exact component={RecoveryPage} />
237+
<Route component={() => <Redirect to="/" />} />
238+
</>
239+
)}
240+
</Switch>
241+
</Router>
242+
);
243+
}
244+
245+
function CatchNetlifyRecoveryNullComponent() {
246+
const {
247+
param: { token, type },
248+
} = useIdentityContext();
249+
const { replace } = useHistory();
250+
const { pathname } = useLocation();
251+
252+
// important to check for the current pathname here because else you land
253+
// in a infinite loop
254+
if (token && type === 'recovery' && pathname === '/') {
255+
replace(`/recovery`, { token });
256+
}
257+
258+
return null;
259+
}
260+
261+
function RecoveryPage() {
262+
const {
263+
location: { state },
264+
} = useHistory();
265+
// this state _might_ not be needed, it was needed in my specific implementation
266+
const [token] = useState(state?.token);
267+
268+
return null; // set new password in a form and call updateUser
269+
}
270+
```
271+
272+
</details>
273+
192274
## Lower level API: `useNetlifyIdentity`
193275

194276
If you'd like to handle your own context yourself, you can use this library as a hook as well:
@@ -201,8 +283,6 @@ function useNetlifyIdentity(
201283
): ReactNetlifyIdentityAPI;
202284
```
203285

204-
the library watches for and handles confirmation routes by default. If you don't like this, pass `enableRunRoutes: false`. This was added here https://github.com/sw-yx/react-netlify-identity/issues/12 in v0.1.8
205-
206286
## License
207287

208288
MIT © [sw-yx](https://github.com/sw-yx)

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-netlify-identity",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "use netlify identity with react",
55
"author": "sw-yx",
66
"license": "MIT",

src/index.tsx

+13-11
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import React, {
44
useEffect,
55
createContext,
66
useContext,
7+
useCallback,
78
// types
8-
Dispatch,
9-
SetStateAction,
109
ReactNode,
11-
useCallback,
1210
} from 'react';
1311

1412
import GoTrue, {
@@ -48,7 +46,7 @@ type MaybeUserPromise = Promise<User | undefined>;
4846
export type ReactNetlifyIdentityAPI = {
4947
user: User | undefined;
5048
/** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */
51-
setUser: Dispatch<SetStateAction<User | undefined>>;
49+
setUser: (_user: GoTrueUser | undefined) => GoTrueUser | undefined;
5250
isConfirmedUser: boolean;
5351
isLoggedIn: boolean;
5452
signupUser: (
@@ -218,16 +216,20 @@ export function useNetlifyIdentity(
218216
);
219217

220218
const recoverAccount = useCallback(
221-
(remember?: boolean | undefined) => {
219+
(remember?: boolean) => {
222220
if (!param.token || param.type !== 'recovery') {
223221
throw new Error(errors.tokenMissingOrInvalid);
224222
}
225223

226-
return goTrueInstance.recover(param.token, remember).then(user => {
227-
// clean up consumed token
228-
setParam(defaultParam);
229-
return _setUser(user);
230-
});
224+
return goTrueInstance
225+
.recover(param.token, remember)
226+
.then(user => {
227+
return _setUser(user);
228+
})
229+
.finally(() => {
230+
// clean up consumed token
231+
setParam(defaultParam);
232+
});
231233
},
232234
[goTrueInstance, _setUser, param]
233235
);
@@ -294,7 +296,7 @@ export function useNetlifyIdentity(
294296
return {
295297
user,
296298
/** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */
297-
setUser,
299+
setUser: _setUser,
298300
isConfirmedUser: !!(user && user.confirmed_at),
299301
isLoggedIn: !!user,
300302
signupUser,

src/token.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export type TokenParam = {
22
token: string | undefined;
33
type: 'invite' | 'recovery' | 'email_change' | undefined;
4-
error?: 'access_denied';
5-
status?: 403;
4+
error: 'access_denied' | undefined;
5+
status: 403 | undefined;
66
};
77

88
export const defaultParam: TokenParam = {

0 commit comments

Comments
 (0)