@@ -28,47 +28,31 @@ npm install formik @lemoncode/fonk @lemoncode/fonk-formik --save
28
28
- To avoid having too much repeated code let's move to common an input component, including it's
29
29
label plus validation text.
30
30
31
- _ ./common/textFieldForm .tsx_
31
+ _ ./common/textField.component .tsx_
32
32
33
33
``` tsx
34
34
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" ;
50
37
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 );
57
42
58
- export const TextFieldForm: React .StatelessComponent <Props > = props => {
59
- const { name, label, onChange, value, error, type } = props ;
60
43
return (
61
44
<>
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 }
64
54
margin = " normal"
65
- value = { value }
66
- type = { type }
67
- onChange = { onTextFieldChange (name , onChange )}
68
55
/>
69
- <Typography variant = " caption" color = " error" gutterBottom >
70
- { props .error }
71
- </Typography >
72
56
</>
73
57
);
74
58
};
@@ -80,12 +64,12 @@ _./src/common/index.ts_
80
64
81
65
``` diff
82
66
export * from './notification';
83
- + export * from './textFieldForm ';
67
+ + export * from './TextFieldComponent ';
84
68
```
85
69
86
70
- Now let's define a basic validation for the form, we want to ensure both fields are informed.
87
71
88
- _ ./src/pages/loginPage .validation.ts_
72
+ _ ./src/pages/login .validation.ts_
89
73
90
74
``` typescript
91
75
import { ValidationSchema , Validators } from " @lemoncode/fonk" ;
@@ -105,151 +89,62 @@ export const loginFormValidation = createFormikValidation(validationSchema);
105
89
106
90
- First let's add the dataFormErrors to the state of the component.
107
91
108
- _ ./src/pages/loginPage .tsx_
92
+ _ ./src/pages/login.container .tsx_
109
93
110
94
``` diff
111
95
import { isValidLogin } from "../api/login";
112
96
import { NotificationComponent } from "../common";
113
97
```
114
98
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_
168
99
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
179
101
displaying validation errors).
180
102
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_
194
104
195
105
``` 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>
217
138
```
218
139
219
140
- Let's give a try
220
141
221
- ```
142
+ ``` bash
222
143
npm start
223
144
```
224
145
225
146
- And let's add an alert (Excercise and a notification) when the user clicks and the form all the fields are valid.
226
147
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
-
253
148
> Excercise, refactor this method following single abstraction level principle and single responsibility principle.
254
149
255
150
# About Basefactor + Lemoncode
0 commit comments