Skip to content

Commit ea6738a

Browse files
committed
Improve contact us form
- Better feedback, while sending and after sending - Clear form once the request is sent - Disable send button while sending - Refactor form code
1 parent 883e61b commit ea6738a

File tree

4 files changed

+73
-67
lines changed

4 files changed

+73
-67
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"prop-types": "^15.7.2",
2222
"react": "^16.13.1",
2323
"react-dom": "^16.13.1",
24-
"react-helmet": "^5.2.1"
24+
"react-helmet": "^5.2.1",
25+
"react-use-form-state": "^0.13.1"
2526
},
2627
"devDependencies": {
2728
"prettier": "^2.0.5"

src/components/form/index.css

+5
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,9 @@
9494
}
9595
}
9696
}
97+
98+
&--feedback-message {
99+
font-size: 1.9rem;
100+
font-weight: bold;
101+
}
97102
}

src/components/form/index.js

+61-66
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,65 @@
1-
import React, { useState, useCallback } from 'react';
1+
import React, { useState, useCallback, useRef } from 'react';
22
import classnames from 'classnames';
3+
import { useFormState } from 'react-use-form-state';
34

45
import './index.css';
56

6-
const encode = data => {
7+
const encode = (data) => {
78
return Object.keys(data)
8-
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
9+
.map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
910
.join('&');
1011
};
1112

12-
export default props => {
13-
const [name, setName] = useState('');
14-
const [email, setEmail] = useState('');
15-
const [message, setMessage] = useState('');
16-
const [city, setCity] = useState('');
13+
export default (props) => {
14+
const [formState, { text, email, textarea }] = useFormState();
1715
const [feedbackMessage, setFeedbackMessage] = useState('');
18-
19-
const onChangeName = useCallback(e => setName(e.target.value), []);
20-
const onChangeEmail = useCallback(e => setEmail(e.target.value), []);
21-
const onChangeMessage = useCallback(e => setMessage(e.target.value), []);
22-
const onChangeCity = useCallback(e => setCity(e.target.value), []);
16+
const [sendingMessage, setSendingMessage] = useState(false);
17+
const formRef = useRef();
2318

2419
const onSubmit = useCallback(
25-
async e => {
20+
async (e) => {
2621
e.preventDefault();
2722

23+
if (!isFormValid(formState)) {
24+
setFeedbackMessage('Please complete all required fields');
25+
return;
26+
}
27+
28+
if (sendingMessage) {
29+
return;
30+
}
31+
32+
setFeedbackMessage('Sending your message now 🚀');
33+
setSendingMessage(true);
34+
2835
try {
2936
const response = await fetch('/', {
3037
method: 'POST',
3138
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
3239
body: encode({
3340
'form-name': 'contact',
34-
name,
35-
email,
36-
city,
37-
message,
41+
name: formState.values.name,
42+
email: formState.values.email,
43+
city: formState.values.city,
44+
message: formState.values.message,
3845
}),
3946
});
4047

41-
console.log('response', response);
48+
if (response.status !== 200) {
49+
throw new Error('unable to send message');
50+
}
4251

4352
setFeedbackMessage('Thank you!');
53+
formState.clear();
54+
formRef.current.reset();
4455
} catch (e) {
4556
console.error('error', e);
4657
setFeedbackMessage('Unable to send the message, please try again');
58+
} finally {
59+
setSendingMessage(false);
4760
}
4861
},
49-
[name, email, city, message]
62+
[formState.values, sendingMessage]
5063
);
5164

5265
return (
@@ -55,69 +68,46 @@ export default props => {
5568
onSubmit={onSubmit}
5669
className="Form"
5770
name="contact"
71+
ref={formRef}
5872
>
5973
<input type="hidden" name="form-name" value="contact" />
6074

61-
<Input
62-
component="input"
63-
type="name"
64-
name="name"
65-
value={name}
66-
onChange={onChangeName}
67-
required
68-
label="Name*"
69-
/>
70-
71-
<Input
72-
component="input"
73-
type="email"
74-
name="email"
75-
value={email}
76-
onChange={onChangeEmail}
77-
required
78-
label="Email*"
79-
/>
80-
81-
<Input
82-
component="input"
83-
type="text"
84-
name="city"
85-
value={city}
86-
onChange={onChangeCity}
87-
label="City"
88-
/>
89-
90-
<Input
91-
required
92-
name="message"
93-
component="textarea"
94-
value={message}
95-
onChange={onChangeMessage}
96-
label="Message*"
97-
/>
75+
<Input {...text('name')} required label="Name*" />
76+
77+
<Input {...email('email')} required label="Email*" />
78+
79+
<Input {...text('city')} label="City" />
80+
81+
<Input {...textarea('message')} type="textarea" label="Message*" />
82+
83+
{feedbackMessage && (
84+
<p className="Form--feedback-message">{feedbackMessage}</p>
85+
)}
9886

9987
<div className="Form--submit-container">
100-
<button type="submit">Send</button>
88+
<button disabled={sendingMessage} type="submit">
89+
Send
90+
</button>
10191
</div>
102-
103-
{feedbackMessage && <p>{feedbackMessage}</p>}
10492
</form>
10593
);
10694
};
10795

108-
const Input = props => {
109-
const { component, label, className, ...others } = props;
96+
const Input = (props) => {
97+
const { type, label, className, ...others } = props;
11098
let input;
11199

112-
switch (component) {
100+
switch (type) {
101+
case 'text':
102+
case 'email':
113103
case 'input':
114-
input = <input {...others} />;
104+
input = <input type={type} {...others} />;
115105
break;
116106
case 'textarea':
117-
input = <textarea {...others} />;
107+
input = <textarea type={type} {...others} />;
118108
break;
119109
default:
120-
throw new Error('Ops');
110+
throw new Error(`Ops: ${type}`);
121111
}
122112

123113
const classes = classnames(className, {
@@ -131,3 +121,8 @@ const Input = props => {
131121
</label>
132122
);
133123
};
124+
125+
const isFormValid = (formState) =>
126+
formState.validity.name &&
127+
formState.validity.email &&
128+
formState.validity.message;

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -11155,6 +11155,11 @@ react-side-effect@^1.1.0:
1115511155
dependencies:
1115611156
shallowequal "^1.0.1"
1115711157

11158+
react-use-form-state@^0.13.1:
11159+
version "0.13.1"
11160+
resolved "https://registry.yarnpkg.com/react-use-form-state/-/react-use-form-state-0.13.1.tgz#891e678cf6248e4668bfffc1112833c2d98a2e82"
11161+
integrity sha512-h0b+KFykqihTpjHKOi8fKZ3aaFqZofkAMof4K2rkCgC8FaU7JnkjzCAPv9lVJjDuf9AAmFHFM1PPMzPtzdwY3Q==
11162+
1115811163
react@^16.13.1, react@^16.8.0:
1115911164
version "16.13.1"
1116011165
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"

0 commit comments

Comments
 (0)