-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lesson 33 - AuthContext; Form Login;
- Loading branch information
1 parent
1016684
commit 672feb5
Showing
12 changed files
with
4,286 additions
and
0 deletions.
There are no files selected for viewing
3,817 changes: 3,817 additions & 0 deletions
3,817
lesson33-react-context-auth-and-forms/package-lock.json
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "lesson24-react-intro", | ||
"version": "1.0.0", | ||
"description": "", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"build": "webpack", | ||
"start": "webpack serve" | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"@types/react": "^18.3.12", | ||
"@types/react-dom": "^18.3.1", | ||
"ts-loader": "^9.5.1", | ||
"typescript": "^5.6.3", | ||
"webpack": "^5.95.0", | ||
"webpack-cli": "^5.1.4", | ||
"webpack-dev-server": "^5.1.0" | ||
}, | ||
"dependencies": { | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1", | ||
"react-router-dom": "6.27.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<html> | ||
<head> | ||
<script src="/bundle.js" type="module"></script> | ||
</head> | ||
<body> | ||
<h3>Demo React and State</h3> | ||
<div id="container"></div> | ||
</body> | ||
</html> |
20 changes: 20 additions & 0 deletions
20
lesson33-react-context-auth-and-forms/src/AuthProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as React from 'react' | ||
|
||
type AuthContextType = { | ||
username: string | undefined; | ||
setUsername: (v: string | undefined) => void | ||
} | ||
|
||
export const AuthContext = React.createContext<AuthContextType>({ | ||
username: undefined, | ||
setUsername: () => { throw Error("Not implemented!") } | ||
}) | ||
|
||
export function AuthProvider({children} : { children: React.ReactNode}) { | ||
const [user, setUser] = React.useState(undefined) | ||
return ( | ||
<AuthContext.Provider value={{username: user, setUsername: setUser}}> | ||
{children} | ||
</AuthContext.Provider> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import * as React from "react" | ||
import { AuthContext } from "./AuthProvider" | ||
import { Navigate, useLocation } from "react-router-dom" | ||
|
||
export function AuthRequire({ children }: { children: React.ReactNode }) { | ||
const { username } = React.useContext(AuthContext) | ||
const location = useLocation() | ||
if (username) { return <>{children}</> } | ||
else { | ||
return <Navigate to={"/login"} state={{source: location.pathname}}></Navigate> | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import * as React from 'react'; | ||
import { Navigate, useLocation } from 'react-router-dom'; | ||
import { AuthContext } from './AuthProvider'; | ||
|
||
/*********************** | ||
* RequireAuth Component | ||
*/ | ||
export function Login() { | ||
const location = useLocation() | ||
const {username, setUsername} = React.useContext(AuthContext) | ||
const [state, dispatch] = React.useReducer(reduce, { | ||
tag: "editing", inputs: { | ||
username: "", | ||
password: "" | ||
} | ||
}) | ||
if (state.tag === "redirect") return ( | ||
<Navigate to={location.state?.source} replace={true}></Navigate> | ||
) | ||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) { | ||
event.preventDefault() | ||
if (state.tag != "editing") { return } | ||
dispatch({ type: "submit" }) | ||
const { username, password } = state.inputs | ||
authenticate(username, password) | ||
.then(res => { | ||
if(res) { setUsername(res) } | ||
dispatch( res | ||
? {type: "success"} | ||
: {type: "error", message: `Invalid username or password: ${username} or ${password}`} | ||
)}) | ||
.catch(err => dispatch({type: "error", message: err.message})) | ||
} | ||
|
||
function handleChange(event: React.ChangeEvent<HTMLInputElement>) { | ||
dispatch({ type: "edit", inputName: event.target.name, inputValue: event.target.value }) | ||
} | ||
const usr = state.tag === "editing" ? state.inputs.username : "" | ||
const password = state.tag === "editing" ? state.inputs.password : "" | ||
return ( | ||
<form onSubmit={handleSubmit}> | ||
<fieldset disabled={state.tag !== 'editing'}> | ||
<div> | ||
<label htmlFor="username">Username</label> | ||
<input id="username" type="text" name="username" value={usr} onChange={handleChange} /> | ||
</div> | ||
<div> | ||
<label htmlFor="password">Password</label> | ||
<input id="password" type="text" name="password" value={password} onChange={handleChange} /> | ||
</div> | ||
<div> | ||
<button type="submit">Login</button> | ||
</div> | ||
</fieldset> | ||
{state.tag === 'editing' && state.error} | ||
</form> | ||
); | ||
} | ||
|
||
/*********************** | ||
* REDUCER | ||
*/ | ||
|
||
function reduce(state: State, action: Action): State { | ||
switch (state.tag) { | ||
case 'editing': | ||
switch (action.type) { | ||
case "edit": return { tag: 'editing', inputs: { ...state.inputs, [action.inputName]: action.inputValue } } | ||
case "submit": return { tag: 'submitting' } | ||
} | ||
case 'submitting': | ||
switch (action.type) { | ||
case "success": return { tag: 'redirect' } | ||
case "error": return { tag: 'editing', error: action.message, inputs: { username: "", password: "" } } | ||
} | ||
case 'redirect': | ||
throw Error("Already in final State 'redirect' and should not reduce to any other State.") | ||
} | ||
} | ||
|
||
type State = { tag: 'editing'; error?: string, inputs: { username: string, password: string }; } | ||
| { tag: 'submitting' } | ||
| { tag: 'redirect' } | ||
|
||
type Action = { type: "edit", inputName: string, inputValue: string } | ||
| { type: "submit" } | ||
| { type: "success" } | ||
| { type: "error", message: string } | ||
|
||
/************************ | ||
* Auxiliary Functions emulating authenticate | ||
*/ | ||
|
||
function delay(delayInMs: number) { | ||
return new Promise(resolve => { | ||
setTimeout(() => resolve(undefined), delayInMs); | ||
}); | ||
} | ||
|
||
async function authenticate(username: string, password: string): Promise<string | undefined> { | ||
await delay(3000); | ||
if ((username == 'alice' || username == 'bob') && password == '1234') { | ||
return username; | ||
} | ||
return undefined; | ||
} |
26 changes: 26 additions & 0 deletions
26
lesson33-react-context-auth-and-forms/src/ThemeProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as React from 'react' | ||
import { createContext, useState } from 'react' | ||
|
||
type Theme = "light" | "dark" | "colorful" | ||
|
||
//Define the Context Type | ||
type ThemeContextType = { | ||
theme: Theme, | ||
setTheme: (newTheme: Theme) => void | ||
} | ||
|
||
// Create the context | ||
export const ThemeContext = createContext<ThemeContextType>({ | ||
theme: "light", | ||
setTheme: () => { throw Error("Not implemented!") } | ||
}) | ||
|
||
// Define a Provider Component | ||
export function ThemeProvider({ children }: { children: React.ReactNode }): React.JSX.Element { | ||
const [theme, setTheme] = useState<Theme>("light") | ||
return ( | ||
<ThemeContext.Provider value={{ theme, setTheme }}> | ||
{children} | ||
</ThemeContext.Provider> | ||
) | ||
} |
15 changes: 15 additions & 0 deletions
15
lesson33-react-context-auth-and-forms/src/ThemeSwitcher.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import * as React from "react" | ||
|
||
import { ThemeContext } from "./ThemeProvider" | ||
|
||
export function ThemeSwitcher() { | ||
const {theme, setTheme} = React.useContext(ThemeContext) | ||
return ( | ||
<div> | ||
<p>Current theme: {theme}</p> | ||
<button className="button" onClick={() => setTheme("light")}>Light Mode</button> | ||
<button className="button" onClick={() => setTheme("dark")}>Dark Mode</button> | ||
<button className="button" onClick={() => setTheme("colorful")}>Colorful Mode</button> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.