Skip to content

Commit e0dfad8

Browse files
authored
Merge pull request #25 from crsanti/issue/05-refactor
Review 05 Refactor (issue #14)
2 parents cfcb956 + 594ce72 commit e0dfad8

File tree

9 files changed

+305
-1
lines changed

9 files changed

+305
-1
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ typings/
55
*.orig
66
.idea/
77
*/src/**/*.js.map
8-
*.log
8+
*.log
9+
**/.vscode/

05 Refactor/package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "samplereact-es6",
3+
"version": "1.0.0",
4+
"description": "A little project to provide a set of step by step guided samples using React and ES6",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "webpack-dev-server --inline",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"author": "",
11+
"license": "MIT",
12+
"dependencies": {
13+
"bootstrap": "^3.3.7",
14+
"react": "^15.3.2",
15+
"react-dom": "^15.3.2"
16+
},
17+
"devDependencies": {
18+
"babel-core": "^6.18.2",
19+
"babel-loader": "^6.2.7",
20+
"babel-preset-es2015": "^6.18.0",
21+
"babel-preset-react": "^6.16.0",
22+
"css-loader": "^0.25.0",
23+
"file-loader": "^0.9.0",
24+
"html-webpack-plugin": "^2.24.1",
25+
"style-loader": "^0.13.1",
26+
"url-loader": "^0.5.7",
27+
"webpack": "^1.13.3",
28+
"webpack-dev-server": "^1.16.2"
29+
},
30+
"repository": {
31+
"type": "git",
32+
"url": "https://github.com/Lemoncode/react-by-sample-es6"
33+
}
34+
}

05 Refactor/readme.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# 05 Refactor
2+
3+
In the previous sample we were setting an initial username value, what would
4+
happen if we expect this value to come from e.g. an AJAX request or if it could
5+
change in time? The current approach won't work.
6+
7+
We can think about two possible solutions:
8+
9+
- The first idea that could come into our mind is to implement a mix: we receive via props the current name value, then we hold an state with the current editing
10+
value... what drawbacks could we encounter? We have to listen on the `componentWillReceiveProps` for any change on the parent user name control and replace our state, we end up with a mixed governance.
11+
12+
- The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername_, whenever the user clicks on the button to
13+
replace the name it will notify the parent control and it will replace the
14+
content of _userName_" with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. ajax callback) it will update as well
15+
_editingUsername_.
16+
17+
We will take as a starting point sample _04 Callback_:
18+
19+
Summary steps:
20+
21+
- Update _nameEdit.jsx_ in order to request the new _editingUsername_, and remove it from the state.
22+
23+
- Update _app.jsx_ to hold the new editing property in the state, pass it to the
24+
children, control and perform the proper update on the callback event from the
25+
child control.
26+
27+
## Prerequisites
28+
29+
Install [Node.js and npm](https://nodejs.org/en/) if they are not already installed on your computer.
30+
31+
> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors.
32+
33+
## Steps to build it
34+
35+
- Copy the content from _04 Callback_ and execute `npm install`.
36+
37+
- Update _nameEdit.jsx_ in order to request the new _editingUsername_, and remove it
38+
from the state.
39+
40+
```jsx
41+
import React from 'react';
42+
43+
export class NameEditComponent extends React.Component {
44+
constructor(props) {
45+
super(props);
46+
}
47+
48+
render() {
49+
return (
50+
<div>
51+
<label>Update Name:</label>
52+
<input value={this.props.editingUserName}
53+
onChange={(e) => this.props.onEditingNameUpdated(e.target.value)} />
54+
<input type="submit" value="Change" className="btn btn-default"
55+
onClick={this.props.onNameUpdateRequest} />
56+
</div>
57+
);
58+
}
59+
}
60+
61+
NameEditComponent.propTypes = {
62+
editingUserName: React.PropTypes.string.isRequired,
63+
onEditingNameUpdated: React.PropTypes.func.isRequired,
64+
onNameUpdateRequest: React.PropTypes.func.isRequired
65+
};
66+
```
67+
68+
- Update _app.jsx_ to hold the new editing property in the state, pass it to the
69+
children control and perform the proper update on the callback event from the
70+
child control.
71+
72+
73+
```jsx
74+
import React from 'react';
75+
import {HelloComponent} from './hello';
76+
import {NameEditComponent} from './nameEdit';
77+
78+
export class App extends React.Component {
79+
constructor(props) {
80+
super(props);
81+
82+
const defaultUserName = 'defaultUserName';
83+
this.state = {userName: defaultUserName, editingUserName: defaultUserName};
84+
}
85+
86+
setUsernameState() {
87+
this.setState({userName: this.state.editingUserName});
88+
}
89+
90+
updateEditingName(editingName) {
91+
this.setState({editingUserName: editingName});
92+
}
93+
94+
render() {
95+
return (
96+
<div>
97+
<HelloComponent userName={this.state.userName} />
98+
<NameEditComponent
99+
editingUserName={this.state.editingUserName}
100+
onEditingNameUpdated={this.updateEditingName.bind(this)}
101+
onNameUpdateRequest={this.setUsernameState.bind(this)} />
102+
</div>
103+
);
104+
}
105+
}
106+
```
107+
108+
Finally we can check the sample is working as _04 Callback_ executing from the command line
109+
`npm start` and opening [http://localhost:8080](http://localhost:8080).

05 Refactor/src/app.jsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import {HelloComponent} from './hello';
3+
import {NameEditComponent} from './nameEdit';
4+
5+
export class App extends React.Component {
6+
constructor(props) {
7+
super(props);
8+
9+
const defaultUserName = 'defaultUserName';
10+
this.state = {userName: defaultUserName, editingUserName: defaultUserName};
11+
}
12+
13+
setUsernameState() {
14+
this.setState({userName: this.state.editingUserName});
15+
}
16+
17+
updateEditingName(editingName) {
18+
this.setState({editingUserName: editingName});
19+
}
20+
21+
render() {
22+
return (
23+
<div>
24+
<HelloComponent userName={this.state.userName} />
25+
<NameEditComponent
26+
editingUserName={this.state.editingUserName}
27+
onEditingNameUpdated={this.updateEditingName.bind(this)}
28+
onNameUpdateRequest={this.setUsernameState.bind(this)} />
29+
</div>
30+
);
31+
}
32+
}

05 Refactor/src/hello.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
3+
export const HelloComponent = (props) => {
4+
return (
5+
<h2>Hello user: {props.userName}!</h2>
6+
);
7+
}
8+
9+
HelloComponent.propTypes = {
10+
userName: React.PropTypes.string.isRequired
11+
};

05 Refactor/src/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title></title>
6+
</head>
7+
<body>
8+
<h1>Sample app</h1>
9+
<div id="root"></div>
10+
</body>
11+
</html>

05 Refactor/src/main.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react';
2+
import {render} from 'react-dom';
3+
import {App} from './app';
4+
5+
render(
6+
<App />
7+
, document.getElementById('root'));

05 Refactor/src/nameEdit.jsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
export class NameEditComponent extends React.Component {
4+
constructor(props) {
5+
super(props);
6+
}
7+
8+
render() {
9+
return (
10+
<div>
11+
<label>Update Name:</label>
12+
<input value={this.props.editingUserName}
13+
onChange={(e) => this.props.onEditingNameUpdated(e.target.value)} />
14+
<input type="submit" value="Change" className="btn btn-default"
15+
onClick={this.props.onNameUpdateRequest} />
16+
</div>
17+
);
18+
}
19+
}
20+
21+
NameEditComponent.propTypes = {
22+
editingUserName: React.PropTypes.string.isRequired,
23+
onEditingNameUpdated: React.PropTypes.func.isRequired,
24+
onNameUpdateRequest: React.PropTypes.func.isRequired
25+
};

05 Refactor/webpack.config.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
var path = require('path');
2+
var webpack = require('webpack');
3+
var HtmlWebpackPlugin = require('html-webpack-plugin');
4+
5+
var basePath = __dirname;
6+
7+
module.exports = {
8+
context: path.join(basePath, 'src'),
9+
resolve: {
10+
extensions: ['', '.js', '.jsx']
11+
},
12+
target: 'web',
13+
entry: [
14+
'./main.jsx',
15+
'../node_modules/bootstrap/dist/css/bootstrap.css'
16+
],
17+
output: {
18+
path: path.join(basePath, 'dist'),
19+
filename: 'bundle.js'
20+
},
21+
22+
devtool: 'source-map',
23+
24+
devServer: {
25+
contentBase: './dist', //Content base
26+
inline: true, //Enable watch and live reload
27+
host: 'localhost',
28+
port: 8080,
29+
stats: 'errors-only'
30+
},
31+
32+
module: {
33+
loaders: [
34+
{
35+
test: /\.jsx?$/,
36+
exclude: /node_modules/,
37+
loader: 'babel',
38+
query: {
39+
presets: ['react', 'es2015']
40+
}
41+
},
42+
{
43+
test: /\.css$/,
44+
loader: 'style-loader!css-loader'
45+
},
46+
// Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack
47+
// Using here url-loader and file-loader
48+
{
49+
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
50+
loader: 'url?limit=10000&mimetype=application/font-woff'
51+
},
52+
{
53+
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
54+
loader: 'url?limit=10000&mimetype=application/octet-stream'
55+
},
56+
{
57+
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
58+
loader: 'file'
59+
},
60+
{
61+
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
62+
loader: 'url?limit=10000&mimetype=image/svg+xml'
63+
}
64+
]
65+
},
66+
plugins: [
67+
// Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
68+
new HtmlWebpackPlugin({
69+
filename: 'index.html', // Name of file in ./dist/
70+
template: 'index.html', // Name of template in ./src
71+
hash: true
72+
})
73+
]
74+
}

0 commit comments

Comments
 (0)