Skip to content
This repository was archived by the owner on Dec 9, 2021. It is now read-only.

Commit a6e5d66

Browse files
committed
Merge branch 'redux-forms'
2 parents 87cb297 + 3edbd93 commit a6e5d66

File tree

6 files changed

+214
-38
lines changed

6 files changed

+214
-38
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"react-router-dom": "^4.1.2",
8787
"redux": "^3.7.2",
8888
"redux-devtools-extension": "^2.13.2",
89+
"redux-form": "7.0.3",
8990
"redux-saga": "0.15.6"
9091
}
9192
}

src/server-side/controllers/ReactController.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import rootSaga from '../../store/rootSaga';
88

99
class ReactController {
1010

11-
mapRoutes(server) {
11+
async mapRoutes(server) {
12+
const baseMarkup = await fse.readFile(path.resolve(__dirname, '../../public/index.html'), 'utf8');
13+
1214
server.route({
1315
method: 'GET',
1416
path: '/{route*}',
@@ -34,7 +36,7 @@ class ReactController {
3436
}
3537
};
3638

37-
let html = await fse.readFile(path.resolve(__dirname, '../../public/index.html'), 'utf8');
39+
let html = baseMarkup.slice();
3840
html = html.replace('{title}', state.metaReducer.title);
3941
html = html.replace('{description}', state.metaReducer.description);
4042
html = html.replace('{content}', renderedHtml);

src/store/rootReducer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import {combineReducers} from 'redux';
22
import UserReducer from './user/UserReducer';
33
import LoadingReducer from './loading/LoadingReducer';
44
import MetaReducer from './meta/MetaReducer';
5+
import {reducer as formReducer} from 'redux-form';
56
import RenderReducer from './render/RenderReducer';
67

78
const rootReducer = combineReducers({
9+
form: formReducer,
810
loadingReducer: LoadingReducer.reduce,
911
metaReducer: MetaReducer.reduce,
1012
renderReducer: RenderReducer.reduce,

src/views/Contact.jsx

Lines changed: 179 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import {connect} from 'react-redux';
33
import MetaAction from '../store/meta/MetaAction';
4-
4+
import {Field, FormProps, reduxForm} from 'redux-form';
55

66
const mapStateToProps = (state) => ({});
77

@@ -11,70 +11,216 @@ const mapDispatchToProps = (dispatch) => ({
1111

1212
class Contact extends React.Component {
1313

14+
_handleSubmitHandler = (formData) => this._onFormSubmit(formData);
15+
1416
componentWillMount() {
1517
this.props.setMeta({title: 'Contact Page'});
1618
}
1719

1820
render() {
21+
const {handleSubmit, reset} = this.props;
22+
1923
return (
2024
<div>
2125
<div className="jumbotron">
2226
<h1 className="display-3">{'Contact'}</h1>
2327
<p className="lead">{'This contact form uses redux-form to do client-side validation.'}</p>
2428
</div>
25-
<form>
29+
<form onSubmit={handleSubmit(this._handleSubmitHandler)}>
30+
<div className="form-group">
31+
<Field
32+
component={this._renderInputField}
33+
label="Name"
34+
name="name"
35+
placeholder=""
36+
type="text"
37+
/>
38+
</div>
2639
<div className="form-group">
27-
<label htmlFor="emailInput">{'Email'}</label>
28-
<input type="email" className="form-control" id="emailInput" placeholder="Enter email" />
40+
<Field
41+
component={this._renderInputField}
42+
label="Email"
43+
name="email"
44+
placeholder="example@example.com"
45+
type="email"
46+
/>
2947
<small id="emailHelp" className="form-text text-muted">{'We\'ll never share your email with anyone else.'}</small>
3048
</div>
3149
<div className="form-group">
3250
<label htmlFor="exampleSelect1">{'Example select'}</label>
33-
<select className="form-control" id="exampleSelect1">
51+
<Field
52+
name="exampleSelect1"
53+
className="form-control"
54+
component="select"
55+
>
3456
<option>1</option>
3557
<option>2</option>
3658
<option>3</option>
3759
<option>4</option>
3860
<option>5</option>
39-
</select>
61+
</Field>
4062
</div>
4163
<div className="form-group">
42-
<label htmlFor="messageTextArea">{'Message'}</label>
43-
<textarea className="form-control" id="messageTextArea" rows="3"></textarea>
64+
<Field
65+
component={this._renderTextArea}
66+
label="Message"
67+
name="message"
68+
placeholder=""
69+
/>
4470
</div>
4571
<fieldset className="form-group">
46-
<legend>{'Radio buttons'}</legend>
47-
<div className="form-check">
48-
<label className="form-check-label">
49-
<input type="radio" className="form-check-input" name="codeQualityRadio" id="codeQualityRadio1" value="option1" defaultChecked />
50-
{'This code is awesome!'}
51-
</label>
52-
</div>
53-
<div className="form-check">
54-
<label className="form-check-label">
55-
<input type="radio" className="form-check-input" name="codeQualityRadio" id="codeQualityRadio2" value="option2" />
56-
{'This code is ok.'}
57-
</label>
58-
</div>
59-
<div className="form-check disabled">
60-
<label className="form-check-label">
61-
<input type="radio" className="form-check-input" name="codeQualityRadio" id="codeQualityRadio3" value="option3" disabled />
62-
{'This code is bad'}
63-
</label>
64-
</div>
72+
<legend>{'Code Quality'}</legend>
73+
74+
<Field
75+
component={this._renderRadio}
76+
label="This code is awesome!"
77+
name="codeQualityRadio"
78+
option="1"
79+
checked={true}
80+
/>
81+
<Field
82+
component={this._renderRadio}
83+
label="This code is ok."
84+
name="codeQualityRadio"
85+
option="2"
86+
/>
87+
<Field
88+
component={this._renderRadio}
89+
label="This code is bad."
90+
name="codeQualityRadio"
91+
option="3"
92+
disabled={true}
93+
/>
6594
</fieldset>
6695
<div className="form-check">
67-
<label className="form-check-label">
68-
<input type="checkbox" className="form-check-input" />
69-
{'Did you star my repo?'}
70-
</label>
96+
<Field
97+
component={this._renderCheckbox}
98+
label="Did you star my repo?"
99+
name="starred"
100+
type="checkbox"
101+
/>
71102
</div>
72-
<button type="submit" className="btn btn-primary">{'Submit'}</button>
103+
<button
104+
type="submit"
105+
className="btn btn-primary"
106+
>
107+
{'Submit'}
108+
</button>
109+
<button
110+
type="submit"
111+
className="btn btn-primary"
112+
onClick={reset}
113+
>
114+
{'Reset'}
115+
</button>
73116
</form>
74117
</div>
75118
);
76119
}
77120

121+
_onFormSubmit(formData){
122+
console.log(formData);
123+
124+
window.alert(JSON.stringify(formData, null, 2));
125+
}
126+
127+
_renderInputField(field) {
128+
const {meta: {touched, error}} = field;
129+
const className = `small text-danger ${touched && error ? '' : 'd-none'}`;
130+
131+
return (
132+
<span>
133+
<label htmlFor={field.input.name}>
134+
{field.label} <span className={className}>{error}</span>
135+
</label>
136+
<input
137+
{...field.input}
138+
className="form-control"
139+
id={field.input.name}
140+
placeholder={field.placeholder}
141+
type={field.type}
142+
/>
143+
</span>
144+
);
145+
}
146+
147+
_renderCheckbox(field) {
148+
return (
149+
<label
150+
className="form-check-label"
151+
htmlFor={field.input.name}
152+
>
153+
<input
154+
{...field.input}
155+
className="form-check-input"
156+
type="checkbox"
157+
/>
158+
{field.label}
159+
</label>
160+
);
161+
}
162+
163+
_renderRadio(field) {
164+
return (
165+
<div className="form-check">
166+
<label className="form-check-label">
167+
<input
168+
{...field.input}
169+
type="radio"
170+
className="form-check-input"
171+
name={field.input.name}
172+
value={field.option}
173+
disabled={field.disabled}
174+
checked={field.checked}
175+
/>
176+
{field.label}
177+
</label>
178+
</div>
179+
);
180+
}
181+
182+
_renderTextArea(field) {
183+
const { meta: { touched, error } } = field;
184+
const className = `small text-danger ${touched && error ? '' : 'd-none'}`;
185+
186+
return (
187+
<span>
188+
<label htmlFor={field.input.name}>
189+
{field.label} <span className={className}>{error}</span>
190+
</label>
191+
<textarea
192+
{...field.input}
193+
className="form-control"
194+
placeholder={field.placeholder}
195+
rows="3"
196+
>
197+
</textarea>
198+
</span>
199+
);
200+
}
201+
78202
}
79203

80-
export default connect(mapStateToProps, mapDispatchToProps)(Contact);
204+
Contact = connect(mapStateToProps, mapDispatchToProps)(Contact);
205+
206+
export default reduxForm({
207+
form: 'contactForm',
208+
validate: (formData) => {
209+
const errors = {};
210+
const validEmailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
211+
212+
if (!validEmailRegex.test(formData.email)) {
213+
errors.email = 'Invalid email address';
214+
}
215+
216+
if (!formData.name) {
217+
errors.name = 'Required';
218+
}
219+
220+
if (!formData.message) {
221+
errors.message = 'Required';
222+
}
223+
224+
return errors;
225+
},
226+
})(Contact);

src/views/landmarks/Header.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Header extends React.Component {
3838
</li>
3939
</ul>
4040
</nav>
41-
<h3 className="text-muted">{'Star My Repo On Github'}</h3>
41+
<h3 className="text-muted">{'Star My Github Repo!'}</h3>
4242
</div>
4343
);
4444
}

yarn.lock

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,6 +1623,10 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
16231623
version "1.2.0"
16241624
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
16251625

1626+
deep-equal@^1.0.1:
1627+
version "1.0.1"
1628+
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
1629+
16261630
deep-extend@~0.4.0:
16271631
version "0.4.2"
16281632
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
@@ -1820,6 +1824,10 @@ error-stack-parser@^1.3.6:
18201824
dependencies:
18211825
stackframe "^0.3.1"
18221826

1827+
es6-error@^4.0.0:
1828+
version "4.0.2"
1829+
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.0.2.tgz#eec5c726eacef51b7f6b73c20db6e1b13b069c98"
1830+
18231831
es6-promise@^3.0.2:
18241832
version "3.3.1"
18251833
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
@@ -2404,6 +2412,10 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0:
24042412
version "1.2.0"
24052413
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
24062414

2415+
hoist-non-react-statics@^2.2.1:
2416+
version "2.2.1"
2417+
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.2.1.tgz#a7e41c760121d0abfc7a2339b331d29a26389e52"
2418+
24072419
home-or-tmp@^2.0.0:
24082420
version "2.0.0"
24092421
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -2963,7 +2975,7 @@ locate-path@^2.0.0:
29632975
p-locate "^2.0.0"
29642976
path-exists "^3.0.0"
29652977

2966-
lodash-es@^4.2.0, lodash-es@^4.2.1:
2978+
lodash-es@^4.17.3, lodash-es@^4.2.0, lodash-es@^4.2.1:
29672979
version "4.17.4"
29682980
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
29692981

@@ -3921,7 +3933,7 @@ promise@^7.1.1:
39213933
dependencies:
39223934
asap "~2.0.3"
39233935

3924-
prop-types@^15.5.10, prop-types@^15.5.4:
3936+
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.9:
39253937
version "15.5.10"
39263938
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
39273939
dependencies:
@@ -4161,6 +4173,19 @@ redux-devtools-extension@^2.13.2:
41614173
version "2.13.2"
41624174
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d"
41634175

4176+
redux-form@7.0.3:
4177+
version "7.0.3"
4178+
resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-7.0.3.tgz#80157d01df7de6c8eb2297ad1fbbb092bafa34f5"
4179+
dependencies:
4180+
deep-equal "^1.0.1"
4181+
es6-error "^4.0.0"
4182+
hoist-non-react-statics "^2.2.1"
4183+
invariant "^2.2.2"
4184+
is-promise "^2.1.0"
4185+
lodash "^4.17.3"
4186+
lodash-es "^4.17.3"
4187+
prop-types "^15.5.9"
4188+
41644189
redux-saga@0.15.6:
41654190
version "0.15.6"
41664191
resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.15.6.tgz#8638dc522de6c6c0a496fe8b2b5466287ac2dc4d"

0 commit comments

Comments
 (0)