Skip to content

Commit 8269e57

Browse files
committed
ref:#187 update doc 13
1 parent d14cad3 commit 8269e57

4 files changed

Lines changed: 92 additions & 85 deletions

File tree

hooks/13_LoginForm/Readme.md

Lines changed: 89 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ npm install
1818

1919
- Let's update as well the name of the component.
2020

21-
_./src/pages/loginPage.tsx_
21+
_./src/pages/login.component.tsx_
2222

2323
```diff
2424
import * as React from "react";
2525
import { Link } from "react-router-dom";
2626

2727
- export const PageA = () => (
28-
+ export const LoginPage = () => (
28+
+ export const LoginComponent: React.FC<PropsForm> = (props) => {
2929
<div>
3030
- <h2>Hello from page A</h2>
3131
+ <h2> Hello from login Page</h2>
@@ -43,7 +43,7 @@ _./src/app.tsx_
4343
import * as React from "react";
4444
import { HashRouter, Switch, Route } from "react-router-dom";
4545
- import { PageA } from "./pages/pageA";
46-
+ import { LoginPage } from "./pages/loginPage";
46+
+ import { LoginComponent } from "./pages/login.component";
4747
import { PageB } from "./pages/pageB";
4848

4949
export const App = () => {
@@ -53,7 +53,7 @@ export const App = () => {
5353
<HashRouter>
5454
<Switch>
5555
- <Route exact={true} path="/" component={PageA} />
56-
+ <Route exact={true} path="/" component={LoginPage} />
56+
+ <Route exact={true} path="/" component={LoginComponent} />
5757
<Route path="/pageB" component={PageB} />
5858
</Switch>
5959
</HashRouter>
@@ -118,7 +118,7 @@ npm install @material-ui/core @material-ui/icons --save
118118

119119
- Now we could create a login form it could look something like:
120120

121-
_./src/pages/loginPage.tsx_
121+
_./src/pages/login.container.tsx_
122122

123123
```javascript
124124
import * as React from "react";
@@ -143,7 +143,7 @@ const useStyles = makeStyles(theme =>
143143

144144
interface Props extends RouteComponentProps {}
145145

146-
const LoginPageInner = (props: Props) => {
146+
export const LoginContainer: React.FC<Props> = (props) => {
147147
const classes = useStyles();
148148

149149
return (
@@ -167,8 +167,6 @@ const LoginPageInner = (props: Props) => {
167167
</Card>
168168
);
169169
};
170-
171-
export const LoginPage = withRouter < Props > LoginPageInner;
172170
```
173171

174172
- This can be ok, but if we take a deeper look to this component, we could break down into two, one is the card itself the other the form dialog, so it should finally look like:
@@ -186,10 +184,10 @@ export const LoginPage = withRouter < Props > LoginPageInner;
186184

187185
- Let's create the LoginForm component (append it to the loginPage file):
188186

189-
_./src/pages/loginPage.tsx_
187+
_./src/pages/login.component.tsx_
190188

191189
```javascript
192-
const LoginForm = props => {
190+
export const LoginComponent: React.FC<PropsForm> = (props) => {
193191
return (
194192
<div
195193
style={{
@@ -198,26 +196,36 @@ const LoginForm = props => {
198196
justifyContent: "center"
199197
}}
200198
>
201-
<TextField label="Name" margin="normal" />
202-
<TextField label="Password" type="password" margin="normal" />
203-
<Button variant="contained" color="primary">
199+
<TextField
200+
label="Name"
201+
margin="normal"
202+
/>
203+
<TextField
204+
label="Password"
205+
type="password"
206+
margin="normal"
207+
/>
208+
<Button
209+
variant="contained"
210+
color="primary"
211+
>
204212
Login
205213
</Button>
206214
</div>
207215
);
208216
};
209217
```
210218

211-
- And let's update the _loginPage.tsx_
219+
- And let's update the _login.container.tsx_
212220

213-
_./src/pages/loginPage.tsx_
221+
_./src/pages/login.container.tsx_
214222

215223
```diff
216224
return (
217225
<Card className={classes.card}>
218226
<CardHeader title="Login" />
219227
<CardContent>
220-
+ <LoginForm/>
228+
+ <LoginComponent/>
221229
- <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
222230
- <TextField
223231
- label="Name"
@@ -247,7 +255,7 @@ npm start
247255

248256
- First we will expose a method to do that in the loginPage.
249257

250-
_./src/pages/login/loginPage.tsx_
258+
_./src/pages/login/login.container.tsx_
251259

252260
```diff
253261
// ...
@@ -264,7 +272,7 @@ const useStyles = makeStyles(theme =>
264272

265273
interface Props extends RouteComponentProps {}
266274

267-
const LoginPageInner = (props) => {
275+
export const LoginContainer: React.FC<Props> = (props) => {
268276
const { classes } = props;
269277

270278
+ const onLogin = () => {
@@ -282,14 +290,13 @@ const LoginPageInner = (props) => {
282290
)
283291
}
284292

285-
export const LoginPage = withRouter<Props>(LoginPageInner);
286293
```
287294

288295
- Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_.
289296
In order to do this we have used react-router 4 "withRouter" HoC (High order component), and pass it
290297
down to the LoginForm component.
291298

292-
_./src/pages/loginPage.tsx_
299+
_./src/pages/login.component.tsx_
293300

294301
```diff
295302
+interface PropsForm {
@@ -351,86 +358,85 @@ _./src/api/login.ts_
351358
import { LoginEntity } from "../model/login";
352359

353360
// Just a fake loginAPI
354-
export const isValidLogin = (loginInfo: LoginEntity): boolean =>
355-
loginInfo.login === "admin" && loginInfo.password === "test";
361+
export const isValidLogin = (loginInfo: LoginEntity): Promise<boolean> =>
362+
new Promise((resolve) => {
363+
setTimeout(() => {
364+
// mock call
365+
resolve(loginInfo.login === "admin" && loginInfo.password === "test");
366+
}, 500);
367+
});
356368
```
357369

358370
- Let's add the _api_ integration, plus navigation on login succeeded:
359371

360372
- First let's create a login state and add the api integration.
361373

362-
_./src/pages/loginPage.tsx_
374+
_./src/pages/login.container.tsx_
363375

364376
```diff
365377
+ import { LoginEntity, createEmptyLogin } from '../model/login';
366378
+ import { isValidLogin } from '../api/login';
367379
```
368380

369-
_./src/pages/loginPage.tsx_
381+
_./src/pages/login.container.tsx_
370382

371383
```diff
372384
const LoginPageInner = (props: Props) => {
373-
+ const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(createEmptyLogin());
385+
+ const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(
386+
+ createEmptyLogin()
387+
+ );
374388
const { classes } = props;
375389

376-
const onLogin = () => {
377-
+ if(isValidLogin(loginInfo)) {
378-
props.history.push("/pageB");
379-
+ }
390+
const loginSucceeded = (isValid: boolean) => {
391+
if (isValid) {
392+
history.push("/pageB");
393+
} else {
394+
setShowAlert(true);
395+
}
396+
};
397+
398+
const handleLogin = (login: LoginEntity) => {
399+
isValidLogin(login).then(loginSucceeded);
380400
};
381401

382402
```
383403

384404
- Now let's read the data from the textfields components (user and password).
385405

386-
_./src/pages/loginPage.tsx_
406+
_./src/pages/login.container.tsx_
387407

388408
```diff
389-
const onLogin = () => {
390-
if (isValidLogin(loginInfo)) {
391-
props.history.push("/pageB");
392-
}
393-
};
394-
395-
+ const onUpdateLoginField = (name, value) => {
396-
+ setLoginInfo({
397-
+ ...loginInfo,
398-
+ [name]: value,
399-
+ })
400-
+ }
401-
402409
return (
403410
<Card className={classes.card}>
404411
<CardHeader title="Login" />
405412
<CardContent>
406-
<LoginForm onLogin={onLogin}
407-
+ onUpdateField={onUpdateLoginField}
408-
+ loginInfo={loginInfo}
409-
/>
413+
+ <LoginForm onLogin={handleLogin}/>
410414
</CardContent>
411415
</Card>
412416
);
413417
```
414418

415419
- And update _LoginForm_ props and textField onChange.
416420

417-
_./src/pages/loginPage.tsx_
421+
_./src/pages/login.component.tsx_
418422

419423
```diff
420424
interface PropsForm {
421-
onLogin: () => void;
422-
+ onUpdateField: (name: string, value: any) => void;
423-
+ loginInfo : LoginEntity;
425+
+ onLogin: (login: LoginEntity) => void;
424426
}
425427

426-
const LoginForm = (props: PropsForm) => {
427-
- const { onLogin } = props;
428-
+ const { onLogin, onUpdateField, loginInfo } = props;
429-
430-
+ // TODO: Enhacement move this outside the stateless component discuss why is a good idea
428+
export const LoginComponent: React.FC<PropsForm> = (props) => {
429+
+ const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(
430+
+ const { onLogin } = props;
431+
+ createEmptyLogin()
432+
+ );
433+
+ const classes = useFormStyles();
431434
+ const onTexFieldChange = (fieldId) => (e) => {
432-
+ onUpdateField(fieldId, e.target.value);
433-
+ }
435+
+ setLoginInfo({
436+
+ ...loginInfo,
437+
+ [fieldId]: e.target.value,
438+
+ });
439+
+ };
434440

435441
return (
436442
<div
@@ -440,15 +446,24 @@ const LoginForm = (props: PropsForm) => {
440446
justifyContent: "center"
441447
}}
442448
>
443-
<TextField label="Name" margin="normal"
449+
<TextField
450+
label="Name"
451+
margin="normal"
444452
+ value={loginInfo.login}
445-
+ onChange={onTexFieldChange('login')}
453+
+ onChange={onTexFieldChange("login")}
446454
/>
447-
<TextField label="Password" type="password" margin="normal"
455+
<TextField
456+
label="Password"
457+
type="password"
458+
margin="normal"
448459
+ value={loginInfo.password}
449-
+ onChange={onTexFieldChange('password')}
460+
+ onChange={onTexFieldChange("password")}
450461
/>
451-
<Button variant="contained" color="primary" onClick={onLogin}>
462+
<Button
463+
variant="contained"
464+
color="primary"
465+
onClick={() => onLogin(loginInfo)}
466+
>
452467
Login
453468
</Button>
454469
</div>
@@ -458,13 +473,11 @@ const LoginForm = (props: PropsForm) => {
458473

459474
- We will add material-ui classes to LoginForm component.
460475

461-
_./src/pages/loginPage.tsx_
476+
_./src/pages/login.container.tsx_
462477

463478
```diff
464479
interface PropsForm {
465-
onLogin: () => void;
466-
onUpdateField: (name: string, value: any) => void;
467-
loginInfo : LoginEntity;
480+
onLogin: (login: LoginEntity) => void;
468481
}
469482

470483
+ // https://material-ui.com/styles/api/#makestyles-styles-options-hook
@@ -478,14 +491,8 @@ interface PropsForm {
478491
+ })
479492
+ );
480493

481-
const LoginForm = (props: PropsForm) => {
494+
export const LoginComponent: React.FC<PropsForm> = (props) => {
482495
+ const classes = useFormStyles();
483-
const { onLogin, onUpdateField, loginInfo } = props;
484-
485-
// TODO: Enhacement move this outside the stateless component discuss why is a good idea
486-
const onTexFieldChange = (fieldId) => (e) => {
487-
onUpdateField(fieldId, e.target.value);
488-
}
489496

490497
return (
491498
- <div
@@ -583,7 +590,7 @@ export * from "./notification";
583590

584591
- Now let's instantiate this in our _loginPage_
585592

586-
_./src/pages/loginPage.tsx_
593+
_./src/pages/login.container.tsx_
587594

588595
```diff
589596
+ import { NotificationComponent } from "../common";
@@ -594,15 +601,15 @@ const LoginPageInner = (props: Props) => {
594601
const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(
595602
createEmptyLogin()
596603
);
597-
+ const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
604+
+ const [isShowAlert, setShowAlert] = React.useState(false);
598605
const classes = useStyles();
599606

600607
const onLogin = () => {
601608
if (isValidLogin(loginInfo)) {
602609
props.history.push("/pageB");
603610
- }
604611
+ } else {
605-
+ setShowLoginFailedMsg(true);
612+
+ setShowAlert(true);
606613
+ }
607614
}
608615

@@ -625,11 +632,11 @@ const LoginPageInner = (props: Props) => {
625632
/>
626633
</CardContent>
627634
</Card>
628-
+ <NotificationComponent
629-
+ message="Invalid login or password, please type again"
630-
+ show={showLoginFailedMsg}
631-
+ onClose={() => setShowLoginFailedMsg(false)}
632-
+ />
635+
+ <NotificationComponent
636+
+ message="Invalid login or password, please type again"
637+
+ show={isShowAlert}
638+
+ onClose={() => setShowAlert(false)}
639+
+ />
633640
+ </>
634641
);
635642
};

hooks/13_LoginForm/src/pages/login.container.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const LoginContainer: React.FC<Props> = (props) => {
4747
<LoginComponent onLogin={handleLogin} />
4848
<NotificationComponent
4949
show={isShowAlert}
50-
message="Invalid Form"
50+
message="Invalid login or password, please type again"
5151
onClose={() => setShowAlert(false)}
5252
/>
5353
</CardContent>

hooks/14_FormValidation/src/pages/login.container.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const LoginContainer: React.FC<Props> = (props) => {
4848
<LoginComponent onLogin={handleLogin} />
4949
<NotificationComponent
5050
show={isShowAlert}
51-
message="Invalid Form"
51+
message="Invalid login or password, please type again"
5252
onClose={() => setShowAlert(false)}
5353
/>
5454
</CardContent>

0 commit comments

Comments
 (0)