@@ -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
3434import * 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
8266export * 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
9175import { 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
11195import { isValidLogin } from "../api/login";
11296import { 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
222143npm 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
0 commit comments