Skip to content

Commit 063361c

Browse files
committed
code refactor
1 parent c727b9d commit 063361c

File tree

5 files changed

+120
-87
lines changed

5 files changed

+120
-87
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const initialValue = {
2525

2626
### Create a validator
2727

28-
Note: Validator key should be the same in the model that you created above. If it doesn't, it will not recognize the validation.
28+
Note: Validator key should be the same in the state key that you created above. If it doesn't, it will not recognize the validation.
2929

3030
```
3131
const validator = {

src/App.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
text-align: center;
33
}
44

5+
div {
6+
margin-bottom: 10px;
7+
}
8+
59
.App-logo {
610
animation: App-logo-spin infinite 20s linear;
711
height: 40vmin;
812
pointer-events: none;
13+
height: 80px;
914
}
1015

1116
.App-header {
1217
background-color: #282c34;
13-
min-height: 100vh;
18+
min-height: 250px;
1419
display: flex;
1520
flex-direction: column;
1621
align-items: center;

src/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import './App.css';
77
function App() {
88
return (
99
<div className="App">
10+
<div className="App-header">
11+
<img className="App-logo" src={logo} alt="react-logo" />
12+
<p>React Form Validation using React Hooks.</p>
13+
</div>
1014
<Form />
1115
</div>
1216
);

src/Form.js

Lines changed: 22 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,19 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React from 'react';
2+
import useForm from './useForm';
23

3-
function useForm(validator, initialValue, callback) {
4-
const [state, setState] = useState(initialValue);
5-
const [disable, setDisable] = useState(true);
6-
const [isDirty, setIsDirty] = useState(false);
7-
8-
useEffect(() => {
9-
if (isDirty) {
10-
setDisable(validateState());
11-
}
12-
}, [state]);
13-
14-
useEffect(() => {
15-
setDisable(validateState());
16-
}, []);
17-
18-
function handleOnChange(event) {
19-
setIsDirty(true);
20-
21-
const name = event.target.name;
22-
const value = event.target.value;
23-
24-
let error = '';
25-
if (validator[name].required) {
26-
if (!value) {
27-
error = 'This is required field.';
28-
}
29-
}
30-
31-
if (
32-
typeof validator[name].validator === 'object' &&
33-
validator[name].validator !== null
34-
) {
35-
if (value && !validator[name].validator.regEx.test(value)) {
36-
error = validator[name].validator.error;
37-
}
38-
}
39-
40-
setState(prevState => ({
41-
...prevState,
42-
[name]: { value, error },
43-
}));
44-
}
45-
46-
function handleOnSubmit(event) {
47-
event.preventDefault();
48-
49-
callback();
50-
}
51-
52-
function validateState() {
53-
return Object.keys(validator).some(key => {
54-
const isRequiredField = validator[key].required;
55-
const hasValue = state[key].value;
56-
57-
return (!hasValue && isRequiredField) || state[key].error;
58-
});
59-
}
4+
function Form() {
5+
// Define your state schema
6+
const stateSchema = {
7+
fname: { value: '', error: '' },
8+
lname: { value: '', error: '' },
9+
tags: { value: '', error: '' },
10+
};
6011

61-
return { state, disable, handleOnChange, handleOnSubmit };
62-
}
12+
// Define your validationStateSchema
6313

64-
function Form() {
65-
const validator = {
14+
// Note: validationStateSchema and stateSchema property
15+
// should be the same in-order validation works!
16+
const validationStateSchema = {
6617
fname: {
6718
required: true,
6819
validator: {
@@ -71,7 +22,7 @@ function Form() {
7122
},
7223
},
7324
lname: {
74-
required: false,
25+
required: true,
7526
validator: {
7627
regEx: /^[a-zA-Z]+$/,
7728
error: 'Invalid last name format.',
@@ -86,28 +37,13 @@ function Form() {
8637
},
8738
};
8839

89-
const initialValue = {
90-
fname: {
91-
value: '',
92-
error: '',
93-
},
94-
lname: {
95-
value: '',
96-
error: '',
97-
},
98-
tags: {
99-
value: '',
100-
error: '',
101-
},
102-
};
103-
104-
function onSubmitForm() {
105-
alert('Form submitted.');
40+
function onSubmitForm(state) {
41+
alert(JSON.stringify(state, null, 2));
10642
}
10743

10844
const { state, handleOnChange, handleOnSubmit, disable } = useForm(
109-
validator,
110-
initialValue,
45+
stateSchema,
46+
validationStateSchema,
11147
onSubmitForm
11248
);
11349

@@ -129,8 +65,9 @@ function Form() {
12965
onChange={handleOnChange}
13066
/>
13167
</label>
68+
{state.fname.error && <p style={errorStyle}>{state.fname.error}</p>}
13269
</div>
133-
{state.fname.error && <p style={errorStyle}>{state.fname.error}</p>}
70+
13471
<div>
13572
<label htmlFor="lname">
13673
Last name:
@@ -141,8 +78,8 @@ function Form() {
14178
onChange={handleOnChange}
14279
/>
14380
</label>
81+
{state.lname.error && <p style={errorStyle}>{state.lname.error}</p>}
14482
</div>
145-
{state.lname.error && <p style={errorStyle}>{state.lname.error}</p>}
14683

14784
<div>
14885
<label htmlFor="tags">
@@ -154,8 +91,8 @@ function Form() {
15491
onChange={handleOnChange}
15592
/>
15693
</label>
94+
{state.tags.error && <p style={errorStyle}>{state.tags.error}</p>}
15795
</div>
158-
{state.tags.error && <p style={errorStyle}>{state.tags.error}</p>}
15996

16097
<input type="submit" name="submit" disabled={disable} />
16198
</form>

src/useForm.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { useState, useEffect, useCallback } from 'react';
2+
3+
function useForm(stateSchema, validationSchema = {}, callback) {
4+
const [state, setState] = useState(stateSchema);
5+
const [disable, setDisable] = useState(true);
6+
const [isDirty, setIsDirty] = useState(false);
7+
8+
// Disable button in initial render.
9+
useEffect(() => {
10+
setDisable(true);
11+
}, []);
12+
13+
// For every changed in our state this will be fired
14+
// To be able to disable the button
15+
useEffect(() => {
16+
if (isDirty) {
17+
setDisable(validateState());
18+
}
19+
}, [state, isDirty]);
20+
21+
// Used to disable submit button if there's an error in state
22+
// or the required field in state has no value.
23+
// Wrapped in useCallback to cached the function to avoid intensive memory leaked
24+
// in every re-render in component
25+
const validateState = useCallback(() => {
26+
const hasErrorInState = Object.keys(validationSchema).some(key => {
27+
const isInputFieldRequired = validationSchema[key].required;
28+
const stateValue = state[key].value; // state value
29+
const stateError = state[key].error; // state error
30+
31+
return (isInputFieldRequired && !stateValue) || stateError;
32+
});
33+
34+
return hasErrorInState;
35+
}, [state, validationSchema]);
36+
37+
// Used to handle every changes in every input
38+
const handleOnChange = useCallback(
39+
event => {
40+
setIsDirty(true);
41+
42+
const name = event.target.name;
43+
const value = event.target.value;
44+
45+
let error = '';
46+
if (validationSchema[name].required) {
47+
if (!value) {
48+
error = 'This is required field.';
49+
}
50+
}
51+
52+
if (
53+
validationSchema[name].validator !== null &&
54+
typeof validationSchema[name].validator === 'object'
55+
) {
56+
if (value && !validationSchema[name].validator.regEx.test(value)) {
57+
error = validationSchema[name].validator.error;
58+
}
59+
}
60+
61+
setState(prevState => ({
62+
...prevState,
63+
[name]: { value, error },
64+
}));
65+
},
66+
[validationSchema]
67+
);
68+
69+
// function handleOnChange(event) {}
70+
71+
const handleOnSubmit = useCallback(
72+
event => {
73+
event.preventDefault();
74+
75+
// Make sure that validateState returns false
76+
// Before calling the submit callback function
77+
if (!validateState()) {
78+
callback(state);
79+
}
80+
},
81+
[state]
82+
);
83+
84+
return { state, disable, handleOnChange, handleOnSubmit };
85+
}
86+
87+
export default useForm;

0 commit comments

Comments
 (0)