This repository was archived by the owner on Sep 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstate-n-lifecycle.html
299 lines (268 loc) · 21.5 KB
/
state-n-lifecycle.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="description" content="Composi is a JavaScript library for creating component-based interfaces. It uses the virtual dom to make efficient updates to the DOM based on a component's data or state.">
<meta name="keywords" content="javascript, framework, performance, small, fast, UI, programming, code, component, composi, chocolatechipui, chocolatechip-ui, reactive, virtual dom">
<title>Composi - Tutorials</title>
<link rel="stylesheet" href="../css/styles.css">
<link rel="stylesheet" href="../css/styles.css">
<link rel="stylesheet" href="../css/prism-tomorrow.css">
</head>
<body class='tutorial-page'>
<nav>
<ul class='nav--menu'>
<li class='nav--menu__item'>
<a class='nav--menu__item__link' href="../index.html">
<svg id='composi-logo' viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Composi Logo</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Composi-Logo-Solid" fill="#fff">
<path d="M1.77635684e-15,0 L95,0 L95,38 L209,38 L209,0 L300,0 L300,94 L265,93.8571663 L265,209 L300,209 L300,300 L209,300 L209,265 L95,265 L95,300 L1.77635684e-15,300 L1.77635684e-15,209 L40,209 L40,94 L1.77635684e-15,93.8571663 L1.77635684e-15,0 Z M107,107 L107,192 L192,192 L192,107 L107,107 Z" id="Combined-Shape"></path>
</g>
</g>
</svg>
<span class='logo__link--text'>Composi</span></a>
</li>
<li class='nav--menu__item'>
<a class='nav--menu__item__link' href="../docs/installation.html">Docs</a>
</li>
<li class='nav--menu__item selected'>
<a class='nav--menu__item__link' href="./index.html">Tutorials</a>
</li>
<li class='nav--menu__item'>
<a class='nav--menu__item__link external' target='__blank' href="https://github.com/composor/awesome-composi">Resources
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="14px" height="14px" viewBox="0 0 511.626 511.627" style="enable-background:new 0 0 511.626 511.627;"
xml:space="preserve">
<path fill='#fff' d="M392.857,292.354h-18.274c-2.669,0-4.859,0.855-6.563,2.573c-1.718,1.708-2.573,3.897-2.573,6.563v91.361
c0,12.563-4.47,23.315-13.415,32.262c-8.945,8.945-19.701,13.414-32.264,13.414H82.224c-12.562,0-23.317-4.469-32.264-13.414
c-8.945-8.946-13.417-19.698-13.417-32.262V155.31c0-12.562,4.471-23.313,13.417-32.259c8.947-8.947,19.702-13.418,32.264-13.418
h200.994c2.669,0,4.859-0.859,6.57-2.57c1.711-1.713,2.566-3.9,2.566-6.567V82.221c0-2.662-0.855-4.853-2.566-6.563
c-1.711-1.713-3.901-2.568-6.57-2.568H82.224c-22.648,0-42.016,8.042-58.102,24.125C8.042,113.297,0,132.665,0,155.313v237.542
c0,22.647,8.042,42.018,24.123,58.095c16.086,16.084,35.454,24.13,58.102,24.13h237.543c22.647,0,42.017-8.046,58.101-24.13
c16.085-16.077,24.127-35.447,24.127-58.095v-91.358c0-2.669-0.856-4.859-2.574-6.57
C397.709,293.209,395.519,292.354,392.857,292.354z"/>
<path fill='#fff' d="M506.199,41.971c-3.617-3.617-7.905-5.424-12.85-5.424H347.171c-4.948,0-9.233,1.807-12.847,5.424
c-3.617,3.615-5.428,7.898-5.428,12.847s1.811,9.233,5.428,12.85l50.247,50.248L198.424,304.067
c-1.906,1.903-2.856,4.093-2.856,6.563c0,2.479,0.953,4.668,2.856,6.571l32.548,32.544c1.903,1.903,4.093,2.852,6.567,2.852
s4.665-0.948,6.567-2.852l186.148-186.148l50.251,50.248c3.614,3.617,7.898,5.426,12.847,5.426s9.233-1.809,12.851-5.426
c3.617-3.616,5.424-7.898,5.424-12.847V54.818C511.626,49.866,509.813,45.586,506.199,41.971z"/>
</svg></a>
</li>
</ul>
</nav>
<article class='tutorial__article'>
<section>
<div class='tutorial'>
<h1>State and Lifecycle</h1>
<p class="tutorial__intro">Previously we made a clock that was re-render every second by a <code>setInterval</code> loop. Now we're going to see how to make a component that updates itself automatically using state.</p>
<pre><code class="language-javascript">function tick() {
const clock = (
<div id='dingo'>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
)
render(
clock,
'body'
)
}
setInterval(tick, 1000)</code></pre>
<p>In the above example, the <code>tick</code> function is passing the <code>clock</code> element to the <code>render</code> function inside a <code>setInterval</code>. This works, but we can do better by converting it into a case-based component.</p>
<h2>Convert a Function Component into a Class-based One</h2>
<p>We can easily convert a function component into one the uses the Component class by following these steps:</p>
<ol>
<li>Create an ES6 class with the same name as the functional component that extends Component.</li>
<li>Add a single empty method <code>render()</code> to the new class.</li>
<li>Move the body of the functional component into the <code>render()</code> method.</li>
<li>Replace <code>props</code> with <code>this.props</code> in the <code>render()</code> body.</li>
<li>Delete the old, functional component.</li>
</ol>
<p>Following these steps with our clock from above, we get this:</p>
<pre><code class="language-javascript">class Clock extends Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
)
}
}</code></pre>
<p>Now we have the basis for a Clock component, but we need to add local state and lifecycle hooks.</p>
<h2>Adding Local State to a Class</h2>
<p>In order to add local state to the class, we'll first need to give it a constructor. The constructor always needs to come first in a class. Because this is an extension of the Component class, we'll also need to invoke <code>super</code> in the constructor so we can pass the class's props to the Component class. Right after the <code>super</code> call we can add in state. Remember that state in the constructor needs to be attached to the <code>this</code> keyword. And finally, since the class now has state, we don't need to use <code>this.props.date</code> to access the date. Instead we switch that to use the class's state.</p>
<p>One more thing about component classes. When we make a functional component and pass it to render, we also pass in a selector for where we want the component to be rendered. For component classes we indicate where we want it render by giving the class a <code>container</code> property. In this case we'll be adding that to the constructor, right after the state:</p>
<pre><code class="language-javascript">class Clock extends Component {
constructor(props) {
super(props)
this.state = {date: new Date()}
this.container = 'body'
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}</code></pre>
<p>Since we now have a class with state, we no longer need to pass it to the <code>render</code> function. Instead we simply create an instance with the <code>new</code> keyword:</p>
<pre><code class="language-javascript">const clock = new Clock()</code></pre>
<p>Codepen Example:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="yPyJVy" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-1" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/yPyJVy/">Composi Tuts -State and Lifecycle-1</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>When we instantiate our clock as above, it will render in the browser. However, as it is the clock will not update every second. We need a way to know that the component was created and inserted into the document before starting a loop to do so.</p>
<p> </p>
<h2>Adding Lifecycle Methods to a Class</h2>
<p>Class components have five different lifecycle methods that we can use. These hooks let your do something right after your component is created, before or after it is updated, and before it is destroyed. In our case, we want to start an interval loop as soon as the component was create. At the same time, if the component gets unmounted and destroyed, we want a way to end the loop. For this we will need to add <code>componentDidMount</code> and <code>componentWillUnmount</code> to our class:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="xPbOgq" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-2" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/xPbOgq/">Composi Tuts -State and Lifecycle-2</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>With the lifecycle hooks setup, instantiating a new instance of the clock will cause the loop to kick in, resulting in a clock that updates itself automatically. Using class components with state is a more efficient way of creating component with encapsulated behaviors.</p>
<h2>Using State Correctly</h2>
<p>Although you can in some cases change state directly, it's always better to use <code>setState</code> to do so. When state is a primitive type, such as string, number or boolean, it is possible to set the state directly through assignment:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="oogLWo" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-3" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/oogLWo/">Composi Tuts -State and Lifecycle-3</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>For objects, we pass in an object with the property and value we want to udpate:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="gXbMoZ" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-4" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/gXbMoZ/">Composi Tuts -State and Lifecycle-4</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>Notice how to update the <code>employer</code> property, we passed it in as its own small object literal. When you use <code>setState</code> on state that is an object, it performs a mixin where the new property/value replaces the previous one. In the case where the new property/value don't exist on the state object, they get added.</p>
<h2>Updating an Array</h2>
<p>Updating the state when it is an array is easy, just use a callback <code>setState</code> to update the array:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="POwzeL" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-5" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/POwzeL/">Composi Tuts -State and Lifecycle-5</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p> </p>
<h2>Updating Array of Objects</h2>
<p>Updating component state when it is an array of objects, it is more not complicate either. Again just use a callback inside <code>setState</code> to update the array's the object values. Suppose we have an array of persons and we assign it to a component instance called <code>personsList</code>:</p>
<pre><code class="language-javascript">const people = [
{
name: 'Joe Bodoni',
job: 'mechanic'
},
{
name: 'Ellen Vanderbilt',
job: 'lab technician'
},
{
name: 'Sam Anderson',
job: 'developer'
}
]</code></pre>
<p> </p>
<h2>Passing a Callback to setState</h2>
<p>The first approach is of course doable, whoever it can be a bit messy. Using the second approach results in more container result. When passing a callback to <code>setState</code>, the component's state gets passed as the argument of the callback. So, using this approach, let's solve the problem of updating Sam's job:</p>
<pre><code class="language-javascript">peopleList.setState(prevState => {
// Update Sam's job:
prevState[2].job = 'cook'
return prevState
})</code></pre>
<p>When using a callback, you must take care to always return the modified state. Not doing so will result in the component state not being updated. Returning the modified state will cause the component state to be updated properly.</p>
<h2>Updates Use requestAnimationFrame</h2>
<p>If you have a situation where state can get updated in rapid succession, you might be worried about unnecessary layout thrashing. However, internally Composi performs update with <code>requestAnimationFrame</code>. If even this is too much for you, there are other options to limit how many times state gets set. You could use <a href='https://www.npmjs.com/package/lodash.debounce' target='__blank'>_.debounce</a> or <a href='https://www.npmjs.com/package/lodash.throttle' target='__blank'>_.throttle</a> from <a href='https://lodash.com' target='__blank'>lodash</a>. You could use these on the event causing state to be set, or use them direction on the code setting state.</p>
<h2>The Data Flows Down</h2>
<p>We alread saw that a component can have child component, and that these are always function components. By their very nature, functional components do not know about their parent, whether it is stateless or stateful. Whatever data a child consumes it gets as props passed down from its parent.</p>
<p>To see how this happens, we can take another look at our class component clock example, modified to use a child component:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="bYNeKX" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-6" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/bYNeKX/">Composi Tuts -State and Lifecycle-6</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>Notice how we've create a functional component, <code>FormattedDate</code>. And in the <code>render</code> function we pass it the component state as a prop--<code>data</code>. <code>FormattedDate</code> does not know what the source of its data is. It doesn't matter. It just expects to receive and input.</p>
<p>Data always flows down from parent to child. This is called a unidirectional data flow. There is no two-way data binding like some libraries offer. One-way data flow makes it easy to reason about what is happening with a component's data.</p>
<h2>State in Class Instatiation</h2>
<p>Instead of assigning state in the class constructor, you can provide it as a property in the class instantiation. In that case you would not need to have a state assignment in the constructor. Taking our above clock example, we we get rid of its constructor and define its state and container in the class instantiation:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="eemzLg" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts -State and Lifecycle-7" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/eemzLg/">Composi Tuts -State and Lifecycle-7</a> by Robert Biggs (<a href="https://codepen.io/rbiggs">@rbiggs</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
<p>Our class is now a bit clearner without the constuctor and we can provide the container and state when we create a new component instance. Providing the container and state during instantiation allows you to create multiple instances of a component in different places in the DOM and with different state.</p>
<h2>Summary</h2>
<p>State is a powerful way to make your components own their data and react when it changes. But you may prefer stateless components and use Redux, Mobx or other state management libraries instead. Check out the docs about <a target='__blank' href=''>how to use</a> Redux and Mobx with Composi.</p>
<p> </p>
</div>
<aside>
<menu>
<ul class="tutorials__menu">
<li class="tutorials__menu__item">
<a href='./index.html'>Hello World</a>
</li>
<li class="tutorials__menu__item">
<a href='./introducing-jsx.html'>Introducing JSX</a>
</li>
<li class="tutorials__menu__item">
<a href='./rendering-elements.html'>Rendering Elements</a>
</li>
<li class="tutorials__menu__item">
<a href='./components-n-props.html'>Components and Props</a>
</li>
<li class="tutorials__menu__item selected">
<a href='./state-n-lifecycle.html'>State and Lifecycle</a>
</li>
<li class="tutorials__menu__item">
<a href='./conditional-rendering.html'>Conditional Rendering</a>
</li>
<li class="tutorials__menu__item">
<a href='./handling-events.html'>Handling Events</a>
</li>
<li class="tutorials__menu__item">
<a href='./lists-n-keys.html'>Lists and Keys</a>
</li>
<li class="tutorials__menu__item">
<a href='./forms.html'>Forms</a>
</li>
<li class="tutorials__menu__item">
<a href='./lifting-state-up.html'>Lifting State Up</a>
</li>
<li class="tutorials__menu__item">
<a href='./thinking-in-composi.html'>Thinking in Composi</a>
</li>
<li class="tutorials__menu__item">
<a href='./jsx-in-depth.html'>JSX In Depth</a>
</li>
<li class="tutorials__menu__item">
<a href='./advanced-state-management.html'>Advanced State Management</a>
</li>
<li class="tutorials__menu__item">
<a href='./composi-datastore.html'>Composi DataStore</a>
</li>
<li class="tutorials__menu__item">
<a href='./integrating-other-libs.html'>Integrating with Other Libraries</a>
</li>
<li class="tutorials__menu__item">
<a href='./composi-without-jsx.html'>Composi Without JSX</a>
</li>
<li class="tutorials__menu__item">
<a href='./immutable-data.html'>Immutable Data</a>
</li>
</ul>
</menu>
</aside>
</section>
</article>
<footer>
<section>
<svg id='composi-logo-footer' viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Composi Logo</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Composi-Logo-Solid" fill="rgba(255,255,255,0.5)">
<path d="M1.77635684e-15,0 L95,0 L95,38 L209,38 L209,0 L300,0 L300,94 L265,93.8571663 L265,209 L300,209 L300,300 L209,300 L209,265 L95,265 L95,300 L1.77635684e-15,300 L1.77635684e-15,209 L40,209 L40,94 L1.77635684e-15,93.8571663 L1.77635684e-15,0 Z M107,107 L107,192 L192,192 L192,107 L107,107 Z" id="Combined-Shape"></path>
</g>
</g>
</svg>
<h3>Composi is open source (MIT) and available on <a href='https://github.com/composor/composi' target='__blank'>Github</a> and <a href="https://www.npmjs.com/package/composi" target='__blank'>NPM</a>.</h3>
</section>
</footer>
<script src="../js/prism.min.js"></script>
<script src="../js/prism-jsx.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-115293685-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-115293685-1');
</script>
</body>
</html>