Skip to content

Commit 21a0f21

Browse files
committed
ref:#187 doc 14 and 15
1 parent 8269e57 commit 21a0f21

File tree

2 files changed

+72
-189
lines changed

2 files changed

+72
-189
lines changed

hooks/14_FormValidation/Readme.md

Lines changed: 54 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -28,47 +28,31 @@ npm install formik @lemoncode/fonk @lemoncode/fonk-formik --save
2828
- To avoid having too much repeated code let's move to common an input component, including it's
2929
label plus validation text.
3030

31-
_./common/textFieldForm.tsx_
31+
_./common/textField.component.tsx_
3232

3333
```tsx
3434
import * as React from "react";
35-
import TextField from "@material-ui/core/TextField";
36-
import Typography from "@material-ui/core/Typography/Typography";
37-
38-
interface Props {
39-
name: string;
40-
label: string;
41-
onChange: any;
42-
value: string;
43-
error?: string;
44-
type?: string;
45-
}
46-
47-
const defaultProps: Partial<Props> = {
48-
type: "text"
49-
};
35+
import { useField } from "formik";
36+
import MuiTextField, { TextFieldProps } from "@material-ui/core/TextField";
5037

51-
const onTextFieldChange = (
52-
fieldId: string,
53-
onChange: (fieldId, value) => void
54-
) => e => {
55-
onChange(fieldId, e.target.value);
56-
};
38+
export const TextFieldComponent: React.FC<TextFieldProps> = (props) => {
39+
const [field, meta] = useField(props.name);
40+
const textFieldProps = Boolean(field) ? field : props;
41+
const hasError = Boolean(meta && meta.touched && meta.error);
5742

58-
export const TextFieldForm: React.StatelessComponent<Props> = props => {
59-
const { name, label, onChange, value, error, type } = props;
6043
return (
6144
<>
62-
<TextField
63-
label={label}
45+
<MuiTextField
46+
{...props}
47+
name={textFieldProps.name}
48+
onChange={textFieldProps.onChange}
49+
onBlur={textFieldProps.onBlur}
50+
value={textFieldProps.value}
51+
error={hasError}
52+
helperText={hasError ? meta.error : ""}
53+
fullWidth={true}
6454
margin="normal"
65-
value={value}
66-
type={type}
67-
onChange={onTextFieldChange(name, onChange)}
6855
/>
69-
<Typography variant="caption" color="error" gutterBottom>
70-
{props.error}
71-
</Typography>
7256
</>
7357
);
7458
};
@@ -80,12 +64,12 @@ _./src/common/index.ts_
8064

8165
```diff
8266
export * from './notification';
83-
+ export * from './textFieldForm';
67+
+ export * from './TextFieldComponent';
8468
```
8569

8670
- Now let's define a basic validation for the form, we want to ensure both fields are informed.
8771

88-
_./src/pages/loginPage.validation.ts_
72+
_./src/pages/login.validation.ts_
8973

9074
```typescript
9175
import { ValidationSchema, Validators } from "@lemoncode/fonk";
@@ -105,151 +89,62 @@ export const loginFormValidation = createFormikValidation(validationSchema);
10589

10690
- First let's add the dataFormErrors to the state of the component.
10791

108-
_./src/pages/loginPage.tsx_
92+
_./src/pages/login.container.tsx_
10993

11094
```diff
11195
import { isValidLogin } from "../api/login";
11296
import { NotificationComponent } from "../common";
11397
```
11498

115-
_./src/pages/loginPage.tsx_
116-
117-
```diff
118-
const LoginPageInner = (props: Props) => {
119-
const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(
120-
createEmptyLogin()
121-
);
122-
+ const [loginFormErrors, setLoginFormErrors] = React.useState<LoginFormErrors>(createDefaultLoginFormErrors());
123-
const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
124-
```
125-
126-
- Let's fire the validation on viewmodel update.
127-
128-
_./src/pages/loginPage.tsx_
129-
130-
```diff
131-
+ import { loginFormValidation } from "./loginPage.validation";
132-
```
133-
134-
_./src/pages/loginPage.tsx_
135-
136-
```diff
137-
const onUpdateLoginField = (name, value) => {
138-
setLoginInfo({
139-
...loginInfo,
140-
[name]: value
141-
});
142-
143-
+ loginFormValidation.validateField(loginInfo, name, value)
144-
+ .then((fieldValidationResult) => {
145-
146-
+ setLoginFormErrors({
147-
+ ...loginFormErrors,
148-
+ [name]: fieldValidationResult,
149-
+ });
150-
+ });
151-
};
152-
```
153-
154-
- We need to pass down dataFormErrors
155-
156-
_./src/pages/loginPage.tsx_
157-
158-
```diff
159-
<LoginForm
160-
onLogin={onLogin}
161-
onUpdateField={onUpdateLoginField}
162-
loginInfo={loginInfo}
163-
+ loginFormErrors={loginFormErrors}
164-
/>
165-
```
166-
167-
_./src/pages/loginPage.tsx_
16899

169-
```diff
170-
interface PropsForm {
171-
onLogin: () => void;
172-
onUpdateField: (string, any) => void;
173-
loginInfo: LoginEntity;
174-
+ loginFormErrors : LoginFormErrors;
175-
}
176-
```
177-
178-
- Let's replace the _TextFieldForm_ entries with the wrapper we have created (includes
100+
- Let's fire the validation on viewmodel update and replace the _TextFieldForm_ entries with the wrapper we have created (includes
179101
displaying validation errors).
180102

181-
_./src/pages/loginPage.tsx_
182-
183-
```diff
184-
+ import { TextFieldForm } from '../common';
185-
```
186-
187-
_./src/pages/loginPage.tsx_
188-
189-
```diff
190-
const LoginForm = (props: PropsForm) => {
191-
- const { onLogin, onUpdateField, loginInfo } = props;
192-
+ const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props;
193-
```
103+
_./src/pages/login.component.tsx_
194104

195105
```diff
196-
- <TextField
197-
+ <TextFieldForm
198-
label="Name"
199-
+ name="login"
200-
- margin="normal"
201-
value={loginInfo.login}
202-
- onChange={onTexFieldChange("login")}
203-
+ onChange={onUpdateField}
204-
+ error={loginFormErrors.login.errorMessage}
205-
/>
206-
- <TextField
207-
+ <TextFieldForm
208-
label="Password"
209-
+ name="password"
210-
type="password"
211-
- margin="normal"
212-
value={loginInfo.password}
213-
- onChange={onTexFieldChange("password")}
214-
+ onChange={onUpdateField}
215-
+ error={loginFormErrors.password.errorMessage}
216-
/>
106+
+ import { loginFormValidation } from "./login.validation";
107+
+ import { TextFieldComponent } from '../common';
108+
109+
+<Formik
110+
+ onSubmit={onLogin}
111+
+ initialValues={createEmptyLogin()}
112+
+ validate={loginFormValidation.validateForm}
113+
+ >
114+
+ {() => (
115+
+ <Form>
116+
+ <div className={classes.formContainer}>
117+
- <TextField
118+
+ <TextFieldComponent
119+
label="Name"
120+
+ name="login"
121+
- margin="normal"
122+
- value={loginInfo.login}
123+
- onChange={onTexFieldChange("login")}
124+
/>
125+
- <TextField
126+
+ <TextFieldComponent
127+
label="Password"
128+
+ name="password"
129+
type="password"
130+
- margin="normal"
131+
- value={loginInfo.password}
132+
- onChange={onTexFieldChange("password")}
133+
+ />
134+
+ </div>
135+
+ </Form>
136+
+ )}
137+
+</Formik>
217138
```
218139

219140
- Let's give a try
220141

221-
```
142+
```bash
222143
npm start
223144
```
224145

225146
- And let's add an alert (Excercise and a notification) when the user clicks and the form all the fields are valid.
226147

227-
_./src/pages/loginPage.tsx_
228-
229-
```diff
230-
const onLogin = () => {
231-
+ loginFormValidation.validateForm(loginInfo)
232-
+ .then((formValidationResult) => {
233-
+ if(formValidationResult.succeeded) {
234-
if (isValidLogin(loginInfo)) {
235-
props.history.push("/pageB");
236-
} else {
237-
setShowLoginFailedMsg(true);
238-
}
239-
+ } else {
240-
+ alert('error, review the fields');
241-
+ const updatedLoginFormErrors = {
242-
+ ...loginFormErrors,
243-
+ ...formValidationResult.fieldErrors,
244-
+ }
245-
+ setLoginFormErrors(updatedLoginFormErrors);
246-
+ }
247-
248-
249-
+ });
250-
};
251-
```
252-
253148
> Excercise, refactor this method following single abstraction level principle and single responsibility principle.
254149
255150
# About Basefactor + Lemoncode

hooks/15_Context/Readme.md

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ import { Link } from "react-router-dom";
119119

120120
- Let's update the loginPage.
121121

122-
_./src/pages/loginPage.tsx_
122+
_./src/pages/login.container.tsx_
123123

124124
```diff
125125
- import { TextFieldForm } from "../common";
@@ -130,37 +130,25 @@ _./src/pages/loginPage.tsx_
130130
_./src/pages/loginPage.tsx_
131131

132132
```diff
133-
const LoginPageInner = (props: Props) => {
134-
+ const loginContext = React.useContext(SessionContext);
133+
export const LoginContainer: React.FC<Props> = (props) => {
134+
+ const loginContext = React.useContext(SessionContext);
135+
const history = useHistory();
136+
const [isShowAlert, setShowAlert] = React.useState(false);
137+
const classes = useFormStyles();
138+
139+
const loginSucceeded = (isValid: boolean, login: LoginEntity) => {
140+
if (isValid) {
141+
history.push("/pageB");
142+
+ loginContext.updateLogin(login.login);
143+
} else {
144+
setShowAlert(true);
145+
}
146+
};
135147

136-
const [loginInfo, setLoginInfo] = React.useState<LoginEntity>(
137-
createEmptyLogin()
138-
);
139-
const [loginFormErrors, setLoginFormErrors] = React.useState<LoginFormErrors>(
140-
createDefaultLoginFormErrors()
141-
);
142-
const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
143-
const classes = useStyles();
144-
145-
const onLogin = () => {
146-
loginFormValidation.validateForm(loginInfo).then(formValidationResult => {
147-
if (formValidationResult.succeeded) {
148-
if (isValidLogin(loginInfo)) {
149-
props.history.push("/pageB");
150-
+ loginContext.updateLogin(loginInfo.login);
151-
} else {
152-
setShowLoginFailedMsg(true);
153-
}
154-
} else {
155-
alert("error, review the fields");
156-
const updatedLoginFormErrors = {
157-
...loginFormErrors,
158-
...formValidationResult.fieldErrors
159-
};
160-
setLoginFormErrors(updatedLoginFormErrors);
161-
}
162-
});
148+
const handleLogin = (login: LoginEntity) => {
149+
isValidLogin(login).then((isValid) => loginSucceeded(isValid, login));
163150
};
151+
};
164152
```
165153

166154
- Let's give a try.

0 commit comments

Comments
 (0)