Skip to content

Commit eb9ecdb

Browse files
author
sw-yx
committed
add login, logout, signup callbacks
1 parent 02a25cd commit eb9ecdb

File tree

7 files changed

+105
-91
lines changed

7 files changed

+105
-91
lines changed

README.md

+20-14
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ the demo is hosted at: https://react-netlify-identity-widget.netlify.com (you ca
88

99
and the source is in `/examples`.
1010

11-
## Blogposts
12-
13-
- [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)
14-
15-
## usage
11+
## Usage
1612

1713
we require some peer dependencies:
1814

@@ -23,13 +19,13 @@ yarn add react-netlify-identity-widget @reach/dialog @reach/tabs @reach/visually
2319
and the styles are optional but provided. here's how to use `IdentityModal, useIdentityContext, IdentityContextProvider`:
2420

2521
```tsx
26-
import React from "react"
27-
import "./App.css"
28-
import IdentityModal, { useIdentityContext, IdentityContextProvider } from "react-netlify-identity-widget"
29-
import "react-netlify-identity-widget/styles.css"
22+
import React from 'react'
23+
import './App.css'
24+
import IdentityModal, { useIdentityContext, IdentityContextProvider } from 'react-netlify-identity-widget'
25+
import 'react-netlify-identity-widget/styles.css'
3026

3127
function App() {
32-
const url = "https://your-identity-instance.netlify.com/" // supply the url of your Netlify site instance. VERY IMPORTANT. no point putting in env var since this is public anyway
28+
const url = 'https://your-identity-instance.netlify.com/' // supply the url of your Netlify site instance. VERY IMPORTANT. no point putting in env var since this is public anyway
3329
return (
3430
<IdentityContextProvider url={url}>
3531
<AuthStatusView />
@@ -42,16 +38,22 @@ function AuthStatusView() {
4238
const identity = useIdentityContext()
4339
const [dialog, setDialog] = React.useState(false)
4440
const name =
45-
(identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.name) || "NoName"
41+
(identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.name) || 'NoName'
4642
const isLoggedIn = identity && identity.isLoggedIn
4743
return (
4844
<div>
4945
<div>
5046
<button className="btn" onClick={() => setDialog(true)}>
51-
{isLoggedIn ? `Hello ${name}, Log out here!` : "Log In"}
47+
{isLoggedIn ? `Hello ${name}, Log out here!` : 'Log In'}
5248
</button>
5349
</div>
54-
<IdentityModal showDialog={dialog} onCloseDialog={() => setDialog(false)} />
50+
<IdentityModal
51+
showDialog={dialog}
52+
onCloseDialog={() => setDialog(false)}
53+
onLogin={(user) => console.log('hello ', user.user_metadata)}
54+
onSignup={(user) => console.log('welcome ', user.user_metadata)}
55+
onLogout={() => console.log('bye ', name)}
56+
/>
5557
</div>
5658
)
5759
}
@@ -61,7 +63,7 @@ You may also code split the Modal if you wish with `React.lazy` and `React.Suspe
6163

6264
```js
6365
// code split the modal til you need it!
64-
const IdentityModal = React.lazy(() => import("react-netlify-identity-widget"))
66+
const IdentityModal = React.lazy(() => import('react-netlify-identity-widget'))
6567

6668
function AuthStatusView() {
6769
// ...
@@ -76,6 +78,10 @@ function AuthStatusView() {
7678
}
7779
```
7880

81+
## Blogposts
82+
83+
- [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)
84+
7985
## Gatsby plugin
8086

8187
You may get a little help configuring RNIW for usage with Gatsby by using https://github.com/sw-yx/gatsby-plugin-netlify-identity. Read its README for more info.

src/app.tsx

+19-18
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import React from "react"
2-
import { Login } from "./components/login"
3-
import { Logout } from "./components/logout"
4-
import { Signup } from "./components/signup"
5-
import { useIdentityContext } from "react-netlify-identity"
6-
import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@reach/tabs"
1+
import React from 'react'
2+
import { Login } from './components/login'
3+
import { Logout } from './components/logout'
4+
import { Signup } from './components/signup'
5+
import { User, useIdentityContext } from 'react-netlify-identity'
6+
import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@reach/tabs'
77

8-
import { Providers } from "./components/providers"
9-
function LoggedOutScreen() {
8+
import { Providers } from './components/providers'
9+
export type AuthProps = {
10+
onLogin?: (user?: User) => void
11+
onSignup?: (user?: User) => void
12+
onLogout?: () => void
13+
}
14+
function LoggedOutScreen(props: AuthProps) {
1015
return (
1116
<div>
1217
<Tabs defaultIndex={0}>
@@ -17,10 +22,10 @@ function LoggedOutScreen() {
1722

1823
<TabPanels>
1924
<TabPanel>
20-
<Login />
25+
<Login onLogin={props.onLogin} />
2126
</TabPanel>
2227
<TabPanel>
23-
<Signup />
28+
<Signup onSignup={props.onSignup} />
2429
</TabPanel>
2530
</TabPanels>
2631
</Tabs>
@@ -29,16 +34,12 @@ function LoggedOutScreen() {
2934
)
3035
}
3136

32-
function LoggedInScreen() {
33-
return <Logout />
37+
function LoggedInScreen(props: AuthProps) {
38+
return <Logout onLogout={props.onLogout} />
3439
}
3540

36-
function Gate({ }: { onCloseDialog: Function }) {
41+
export function Widget(props: AuthProps) {
3742
const identity = useIdentityContext()
3843
const isLoggedIn = Boolean(identity && identity.user)
39-
return isLoggedIn ? <LoggedInScreen /> : <LoggedOutScreen />
40-
}
41-
42-
export function Widget({ onCloseDialog }: { onCloseDialog: Function }) {
43-
return <Gate onCloseDialog={onCloseDialog} />
44+
return isLoggedIn ? <LoggedInScreen {...props} /> : <LoggedOutScreen {...props} />
4445
}

src/components/login.tsx

+15-23
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
1-
import React from "react"
2-
import { useIdentityContext } from "react-netlify-identity"
3-
import useLoading from "../useLoading"
4-
import VisuallyHidden from "@reach/visually-hidden"
1+
import React from 'react'
2+
import { useIdentityContext, User } from 'react-netlify-identity'
3+
import useLoading from '../useLoading'
4+
import VisuallyHidden from '@reach/visually-hidden'
55

6-
export function Login() {
6+
type LoginProps = {
7+
onLogin?: (user?: User) => void
8+
}
9+
10+
export function Login({ onLogin }: LoginProps) {
711
const { loginUser } = useIdentityContext()
812
const formRef = React.useRef<HTMLFormElement>(null)
9-
const [msg, setMsg] = React.useState("")
13+
const [msg, setMsg] = React.useState('')
1014
const [isLoading, load] = useLoading()
11-
// const signup = () => {
12-
// if (!formRef.current) return
13-
// const email = formRef.current.email.value
14-
// const password = formRef.current.password.value
15-
// const data = { signupSource: "react-netlify-identity-widget" }
16-
// load(signupUser(email, password, data))
17-
// .then(user => {
18-
// console.log("Success! Signed up", user)
19-
// // navigate("/dashboard")
20-
// })
21-
// .catch(err => void console.error(err) || setMsg("Error: " + err.message))
22-
// }
2315
return (
2416
<form
2517
ref={formRef}
@@ -31,10 +23,10 @@ export function Login() {
3123
const password = target.password.value
3224
load(loginUser(email, password, true))
3325
.then((user) => {
34-
console.log("Success! Logged in", user)
35-
// navigate("/dashboard")
26+
if (process.env.NODE_ENV !== 'production') console.log('Success! Logged in', user)
27+
if (onLogin) onLogin(user)
3628
})
37-
.catch((err) => void console.error(err) || setMsg("Error: " + err.message))
29+
.catch((err) => void console.error(err) || setMsg('Error: ' + err.message))
3830
}}
3931
>
4032
<div className="formGroup" key="email">
@@ -60,10 +52,10 @@ export function Login() {
6052
</div>
6153

6254
<div>
63-
<button type="submit" className={isLoading ? "btn saving" : "btn"}>
55+
<button type="submit" className={isLoading ? 'btn saving' : 'btn'}>
6456
Log in
6557
</button>
66-
{msg && <pre style={{ background: "salmon", padding: 10 }}>{msg}</pre>}
58+
{msg && <pre style={{ background: 'salmon', padding: 10 }}>{msg}</pre>}
6759
</div>
6860
<button type="button" className="btnLink forgotPasswordLink">
6961
Forgot password?

src/components/logout.tsx

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
import React from "react"
2-
import { useIdentityContext } from "react-netlify-identity"
3-
import useLoading from "../useLoading"
1+
import React from 'react'
2+
import { useIdentityContext } from 'react-netlify-identity'
3+
import useLoading from '../useLoading'
44

5-
export function Logout() {
5+
type LogoutProps = {
6+
onLogout?: () => void
7+
}
8+
9+
export function Logout({ onLogout }: LogoutProps) {
610
const identity = useIdentityContext()
11+
const [msg, setMsg] = React.useState('')
712
const name =
8-
(identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.full_name) || "NoName"
13+
(identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.full_name) || 'NoName'
914

1015
const [isLoading, load] = useLoading()
11-
const logout = () => load(identity.logoutUser())
16+
const logout = () =>
17+
load(identity.logoutUser())
18+
.then(() => {
19+
if (onLogout) onLogout()
20+
})
21+
.catch((err) => void console.error(err) || setMsg('Error: ' + err.message))
1222
return (
1323
<>
1424
<div className="header">
@@ -19,9 +29,10 @@ export function Logout() {
1929
Logged in as <br />
2030
<span className="infoTextEmail">{name}</span>
2131
</p>
22-
<button type="submit" className={isLoading ? "btn saving" : "btn"} onClick={logout}>
32+
<button type="submit" className={isLoading ? 'btn saving' : 'btn'} onClick={logout}>
2333
Log out
2434
</button>
35+
{msg && <pre style={{ background: 'salmon', padding: 10 }}>{msg}</pre>}
2536
</form>
2637
</>
2738
)

src/components/recover.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import React from "react"
1+
import React from 'react'
22

33
export function Recover() {
44
return (
55
<div>
6+
<h1>TODO: NOT IMPLEMENTED YET</h1>
67
<label>
78
Email
89
<input type="email" />

src/components/signup.tsx

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1-
import React from "react"
2-
import { useIdentityContext } from "react-netlify-identity"
3-
import useLoading from "../useLoading"
4-
import VisuallyHidden from "@reach/visually-hidden"
1+
import React from 'react'
2+
import { User, useIdentityContext } from 'react-netlify-identity'
3+
import useLoading from '../useLoading'
4+
import VisuallyHidden from '@reach/visually-hidden'
55

6-
export function Signup() {
6+
type SignupProps = {
7+
onSignup?: (user?: User) => void
8+
}
9+
export function Signup({ onSignup }: SignupProps) {
710
const { signupUser } = useIdentityContext()
811
const formRef = React.useRef<HTMLFormElement>(null)
9-
const [msg, setMsg] = React.useState("")
12+
const [msg, setMsg] = React.useState('')
1013
const [isLoading, load] = useLoading()
1114
const signup = () => {
1215
if (!formRef.current) return
1316
const full_name = formRef.current.username.value
1417
const email = formRef.current.email.value
1518
const password = formRef.current.password.value
16-
const data = { signupSource: "react-netlify-identity-widget", full_name }
19+
const data = { signupSource: 'react-netlify-identity-widget', full_name }
1720
load(signupUser(email, password, data))
1821
.then((user) => {
19-
console.log("Success! Signed up", user)
20-
// navigate("/dashboard")
22+
if (process.env.NODE_ENV !== 'production') console.log('Success! Signed up', user)
23+
if (onSignup) onSignup(user)
2124
})
22-
.catch((err) => void console.error(err) || setMsg("Error: " + err.message))
25+
.catch((err) => void console.error(err) || setMsg('Error: ' + err.message))
2326
}
2427
return (
2528
<form
@@ -67,10 +70,10 @@ export function Signup() {
6770
</label>
6871
</div>
6972
<div>
70-
<button type="submit" className={isLoading ? "btn saving" : "btn"}>
73+
<button type="submit" className={isLoading ? 'btn saving' : 'btn'}>
7174
Sign Up
7275
</button>
73-
{msg && <pre style={{ background: "salmon", padding: 10 }}>{msg}</pre>}
76+
{msg && <pre style={{ background: 'salmon', padding: 10 }}>{msg}</pre>}
7477
</div>
7578
</form>
7679
)

src/index.tsx

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import React from "react"
1+
import React from 'react'
22
import {
3-
Dialog
3+
Dialog,
44
// DialogOverlay, DialogContent
5-
} from "@reach/dialog"
6-
import VisuallyHidden from "@reach/visually-hidden"
5+
} from '@reach/dialog'
6+
import VisuallyHidden from '@reach/visually-hidden'
77

8-
import { Widget } from "./app"
8+
import { Widget, AuthProps } from './app'
99

10-
import { ErrorBoundary } from "./errorBoundary"
10+
import { ErrorBoundary } from './errorBoundary'
1111

1212
import {
1313
IdentityContextProvider as _IdentityContextProvider,
14-
useIdentityContext as _useIdentityContext
15-
} from "react-netlify-identity"
16-
export { User, Settings, ReactNetlifyIdentityAPI, useNetlifyIdentity } from "react-netlify-identity"
14+
useIdentityContext as _useIdentityContext,
15+
} from 'react-netlify-identity'
16+
export { User, Settings, ReactNetlifyIdentityAPI, useNetlifyIdentity } from 'react-netlify-identity'
1717

1818
/** URL of your Netlify Instance with Identity enabled e.g. https://netlify-gotrue-in-react.netlify.com */
1919

@@ -22,28 +22,28 @@ type ModalProps = {
2222
showDialog: boolean
2323
/** modal will call this function to set the state of showDialog to false */
2424
onCloseDialog: () => void
25-
}
25+
} & AuthProps
2626

2727
export const IdentityContextProvider = _IdentityContextProvider
2828
export const useIdentityContext = _useIdentityContext
29-
export function IdentityModal({ showDialog, onCloseDialog }: ModalProps) {
29+
export function IdentityModal({ showDialog, onCloseDialog, ...authprops }: ModalProps) {
3030
return (
3131
<Dialog
3232
isOpen={showDialog}
3333
onDismiss={onCloseDialog}
3434
style={{
35-
border: "solid 5px hsla(0, 0%, 0%, 0.5)",
36-
borderRadius: "10px",
37-
position: "relative",
38-
maxWidth: 400
35+
border: 'solid 5px hsla(0, 0%, 0%, 0.5)',
36+
borderRadius: '10px',
37+
position: 'relative',
38+
maxWidth: 400,
3939
}}
4040
>
4141
<button className="btn btnClose" onClick={onCloseDialog}>
4242
<VisuallyHidden>Close</VisuallyHidden>
4343
</button>
4444
<ErrorBoundary>
4545
<React.Suspense fallback={<div>Loading...</div>}>
46-
<Widget onCloseDialog={onCloseDialog} />
46+
<Widget {...authprops} />
4747
</React.Suspense>
4848
</ErrorBoundary>
4949
</Dialog>

0 commit comments

Comments
 (0)