Skip to content

Commit a4d23a4

Browse files
committed
Port 13 ShouldUpdate from TS to JS. This closes #22
1 parent 9ea4eb3 commit a4d23a4

File tree

13 files changed

+554
-0
lines changed

13 files changed

+554
-0
lines changed

13 ShouldUpdate/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "samplereact",
3+
"version": "1.0.0",
4+
"description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.",
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": "ISC",
12+
"devDependencies": {
13+
"babel-core": "^6.18.2",
14+
"babel-loader": "^6.2.7",
15+
"babel-plugin-transform-runtime": "^6.15.0",
16+
"babel-preset-es2015": "^6.18.0",
17+
"babel-preset-react": "^6.16.0",
18+
"css-loader": "^0.25.0",
19+
"file-loader": "^0.9.0",
20+
"html-webpack-plugin": "^2.24.1",
21+
"style-loader": "^0.13.1",
22+
"url-loader": "^0.5.7",
23+
"webpack": "^1.13.3",
24+
"webpack-devserver": "0.0.6"
25+
},
26+
"dependencies": {
27+
"bootstrap": "^3.3.7",
28+
"react": "^15.3.2",
29+
"react-dom": "^15.3.2"
30+
}
31+
}

13 ShouldUpdate/readme.md

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
# 13 ShouldUpdate
2+
3+
In this sample we will enhance rendering performance hooking to 'shouldComponentUpdate'.
4+
5+
We are going to implement a customer satisfaction widget, based on smily faces,
6+
it will accept a range value (0 to 500), and the faces will have a range of values
7+
0..100, 100..200, 200..300, 300..400, 400..500. We will only fire the render option whenever
8+
the value jumps into the next or previous range.
9+
10+
We will take a startup point sample _03 State_:
11+
12+
Summary steps:
13+
14+
- Remove _hello_ and _nameEdit_ components (app cleanup).
15+
- Copy under dir _content_ the four png's that contain the simleys.
16+
- Create under dir _content_ a _site.css_ file and define stlyes for the smileys.
17+
- Create a smily component.
18+
- Add to app state a currenValue entry, pass it to the control plus add an slider
19+
to configure it.
20+
- Let's add an optimization... componentshouldupdate.
21+
22+
## Prerequisites
23+
24+
Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are not already installed on your computer.
25+
26+
> 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.
27+
28+
## Steps to build it
29+
30+
- Copy the content from _03 State_ and execute:
31+
32+
```
33+
npm install
34+
```
35+
36+
- Remove _nameEdit.js_ and _hello.jsx_, let's wipe them from _app.jsx_ as well:
37+
38+
```jsx
39+
import * as React from 'react';
40+
41+
export class App extends React.Component {
42+
constructor(props) {
43+
super(props);
44+
}
45+
46+
render() {
47+
return (
48+
<div />
49+
);
50+
}
51+
}
52+
53+
```
54+
55+
- Let's create a folder _src/content_ and copy the five smiley faces (you can
56+
copy them from the sample implementation in github).
57+
58+
- Let's create a css file: _src/content/site.css_ and add the smileys styles:
59+
60+
```css
61+
.very-dissatisfied {
62+
width:100%;
63+
height:80px;
64+
background:url('./one.png') no-repeat;;
65+
}
66+
67+
.somewhat-dissatisfied {
68+
width:100%;
69+
height:80px;
70+
background:url('./two.png') no-repeat;
71+
}
72+
73+
.neither {
74+
width:100%;
75+
height:80px;
76+
background:url('./three.png') no-repeat;
77+
}
78+
79+
.somewhat-satisfied {
80+
width:100%;
81+
height:80px;
82+
background:url('./four.png') no-repeat;
83+
}
84+
85+
.very-satisfied {
86+
width:100%;
87+
height:80px;
88+
background:url('./five.png') no-repeat;
89+
}
90+
```
91+
92+
- In _webpack.config.js_ let's add the new _css_ file as entry point:
93+
94+
```javascript
95+
entry: [
96+
'./main.jsx',
97+
'../node_modules/bootstrap/dist/css/bootstrap.css',
98+
'./content/site.css',
99+
],
100+
```
101+
102+
- We need to add as well a loder to handle images in _webpackconfig.js_:
103+
104+
```javascript
105+
{
106+
test: /\.(png|jpg)$/,
107+
exclude: /node_modules/,
108+
loader: 'url-loader?limit=10000'
109+
},
110+
```
111+
112+
- Let's create a simple _faceComponent_ under _src_, we will start by just adding
113+
something hardcoded in file _src/face.jsx_:
114+
115+
```jsx
116+
import * as React from 'react';
117+
118+
const FaceComponent = (props) => (
119+
<div className="somewhat-satisfied" />
120+
);
121+
122+
FaceComponent.propTypes = {
123+
level: React.PropTypes.number.isRequired,
124+
};
125+
126+
export default FaceComponent;
127+
```
128+
129+
- Let's make a quick test on _app.jsx_
130+
131+
```jsx
132+
import * as React from 'react';
133+
import FaceComponent from './face';
134+
135+
export class App extends React.Component {
136+
constructor(props) {
137+
super(props);
138+
}
139+
140+
render() {
141+
return (
142+
<div>
143+
<FaceComponent level={100} />
144+
</div>
145+
);
146+
}
147+
}
148+
149+
```
150+
151+
- Let's make a check point and run the sample: check that is working as expected.
152+
153+
```
154+
npm start
155+
```
156+
157+
- Now it's time to link the property with the proper faces, let's create a style function
158+
for that in _face.jsx_
159+
160+
```jsx
161+
import * as React from 'react';
162+
163+
// eslint-disable-next-line import/prefer-default-export
164+
const FaceComponent = (props) => {
165+
function setSatisfactionClass(level) {
166+
if (level < 100) {
167+
return 'very-dissatisfied';
168+
}
169+
170+
if (level < 200) {
171+
return 'somewhat-dissatisfied';
172+
}
173+
174+
if (level < 300) {
175+
return 'neither';
176+
}
177+
178+
if (level < 400) {
179+
return 'somewhat-satisfied';
180+
}
181+
182+
return ('very-satisfied');
183+
}
184+
185+
return (
186+
<div className={setSatisfactionClass(props.level)} />
187+
);
188+
};
189+
190+
FaceComponent.propTypes = {
191+
level: React.PropTypes.number.isRequired,
192+
};
193+
194+
export default FaceComponent;
195+
196+
```
197+
198+
- In _app.jsx_ let's add a state variable to hold the current satisfaction level plus
199+
an slider to let the user update it.
200+
201+
```jsx
202+
import * as React from 'react';
203+
import FaceComponent from './face';
204+
205+
export class App extends React.Component {
206+
constructor(props) {
207+
super(props);
208+
209+
this.state = { satisfactionLevel: 300 };
210+
}
211+
212+
render() {
213+
return (
214+
<div>
215+
<input
216+
type="range"
217+
min="0"
218+
max="500"
219+
value={this.state.satisfactionLevel}
220+
onChange={event =>
221+
this.setState({ satisfactionLevel: parseInt(event.target.value, 10) })
222+
}
223+
/>
224+
<br />
225+
<span>{this.state.satisfactionLevel}</span>
226+
<br />
227+
<FaceComponent level={this.state.satisfactionLevel} />
228+
</div>
229+
);
230+
}
231+
}
232+
233+
```
234+
235+
- Let's run the sample:
236+
237+
```
238+
npm start
239+
```
240+
241+
- Let's add a rendering optimization, we should only trigger the render whenever
242+
the level just changes the satisfaction range, we need to move the component to
243+
state component:
244+
245+
```jsx
246+
import * as React from 'react';
247+
248+
class FaceComponent extends React.Component {
249+
setSatisfactionClass(level) {
250+
if (level < 100) {
251+
return 'very-dissatisfied';
252+
}
253+
254+
if (level < 200) {
255+
return 'somewhat-dissatisfied';
256+
}
257+
258+
if (level < 300) {
259+
return 'neither';
260+
}
261+
262+
if (level < 400) {
263+
return 'somewhat-satisfied';
264+
}
265+
266+
return ('very-satisfied');
267+
}
268+
269+
shouldComponentUpdate(nextProps, nextState) {
270+
const rangeChange = [100, 200, 300, 400];
271+
272+
let index = 0;
273+
let isRangeChange = false;
274+
while (!isRangeChange && index < rangeChange.length) {
275+
isRangeChange =
276+
(this.props.level < rangeChange[index] &&
277+
nextProps.level >= rangeChange[index]) ||
278+
(this.props.level > rangeChange[index] &&
279+
nextProps.level <= rangeChange[index]);
280+
281+
index += 1;
282+
}
283+
284+
return isRangeChange;
285+
}
286+
render() {
287+
return (
288+
<div className={this.setSatisfactionClass(this.props.level)} />
289+
);
290+
}
291+
}
292+
293+
FaceComponent.propTypes = {
294+
level: React.PropTypes.number.isRequired,
295+
};
296+
297+
export default FaceComponent;
298+
299+
```
300+
301+
- Now if we place a breakpoint in the faceComponent render method we can see that
302+
render is only triggered when you change from a satisfaction range (e.g. 99 to 100).
303+
304+
```
305+
npm start
306+
```

13 ShouldUpdate/src/app.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as React from 'react';
2+
import FaceComponent from './face';
3+
4+
export class App extends React.Component {
5+
constructor(props) {
6+
super(props);
7+
8+
this.state = { satisfactionLevel: 300 };
9+
}
10+
11+
render() {
12+
return (
13+
<div>
14+
<input
15+
type="range"
16+
min="0"
17+
max="500"
18+
value={this.state.satisfactionLevel}
19+
onChange={event =>
20+
this.setState({ satisfactionLevel: parseInt(event.target.value, 10) })
21+
}
22+
/>
23+
<br />
24+
<span>{this.state.satisfactionLevel}</span>
25+
<br />
26+
<FaceComponent level={this.state.satisfactionLevel} />
27+
</div>
28+
);
29+
}
30+
}
9.37 KB
Loading
9.16 KB
Loading
9.37 KB
Loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.very-dissatisfied {
2+
width:100%;
3+
height:80px;
4+
background:url('./one.png') no-repeat;;
5+
}
6+
7+
.somewhat-dissatisfied {
8+
width:100%;
9+
height:80px;
10+
background:url('./two.png') no-repeat;
11+
}
12+
13+
.neither {
14+
width:100%;
15+
height:80px;
16+
background:url('./three.png') no-repeat;
17+
}
18+
19+
.somewhat-satisfied {
20+
width:100%;
21+
height:80px;
22+
background:url('./four.png') no-repeat;
23+
}
24+
25+
.very-satisfied {
26+
width:100%;
27+
height:80px;
28+
background:url('./five.png') no-repeat;
29+
}
8.78 KB
Loading
9.12 KB
Loading

0 commit comments

Comments
 (0)