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 pathcomponents-n-props.html
363 lines (320 loc) · 21.6 KB
/
components-n-props.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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
<!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>Components and Props</h1>
<p class="tutorial__intro">Components let you to break the UI down into small pieces. This helps you think about each bit of functionality in isolation.</p>
<p>At a high level, components are like functions. They take arbitrary arguments and return elements to inject in the document.</p>
<h2>Functional and Class Components</h2>
<p>The simplest way to create a component is to write a function. We use props to pass down the data to the component. To do this, we wrap the function component's argument in curly braces. And when we pass the JSX tag to the render function, we pass the data to use inside curly braces again using the spread operator:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="mqyPYp" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-1" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/mqyPYp/">Composi Tuts - Components and Props-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>The above example is what is called a functional component. It's a function that returns markup to create. Another type of component is create using the Component class:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="ZaYWdz" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-2" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/ZaYWdz/">Composi Tuts - Components and Props-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>For a class-based component, the <code>render</code> is defined directly on the component. We cause the class-based component to be rendered by passing data to its <code>update</code> function.</p>
<p>In the above two examples, the result of the function component and class-based component are identical. Class-based component offer more functionality to create complex components.</p>
<h2>Composing Components</h2>
<p>A component can contain other components. A functional component can have other functional components as children. A class-based component can have functional components as children. However, a class-based component cannot have another class-based components as its children. Composi does not encourage creating a giant app with one overruling component that renders your entire app of many child components. Do not build components that have deeply nested trees of descendent components. Instead, break your app down into smaller, independent components. If they do need to communicate, you can use any pubsub library for that.</p>
<p>Here's an example of a functional component with other functional components as its children:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="oogxrZ" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-3" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/oogxrZ/">Composi Tuts - Components and Props-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>Notice that we enclose all our <code>Welcome</code> tags in a <code>div</code> tag. That's because there must always be one enclosing tag for any markup we want to produce.</p>
<h2>Breaking Components Down</h2>
<p>You should always try to break your components down into smaller pieces that get assembled into the final component. Take the following component as an example:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="MOYyNg" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-4" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/MOYyNg/">Composi Tuts - Components and Props-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>This component is too complicated, so we are going to see how to break it down into smaller pieces. This is tricky because we need to pass data down to the child elements.</p>
<p>The first thing we'll do is extract the avatar part:</p>
<pre><code class="language-javascript">function Avatar(props) {
return (
<img class="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
)
}</code></pre>
<p>When breaking a component down, consider renaming the props to what makes sense at the component level. Since the above component knows nothing of the author, we changed its prop to user. With the above sub-component, we can simplify our main component a bit:</p>
<pre><code class="language-javascript">function Avatar(props) {
return (
<img class="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
)
}
function Comment(props) {
return (
<div class="Comment">
<div class="UserInfo">
<Avatar user={props.author} />
<div class="UserInfo-name">
{props.author.name}
</div>
</div>
<div class="Comment-text">
{props.text}
</div>
<div class="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}</code></pre>
<p>Next we extract the user info, which includes the avatar component:</p>
<pre><code class="language-javascript">function UserInfo(props) {
return (
<div class="UserInfo">
<Avatar user={props.user} />
<div class="UserInfo-name">
{props.user.name}
</div>
</div>
)
}</code></pre>
<p>With this change, we can simplify the component setup ever more:</p>
<pre><code class="language-javascript">function Avatar(props) {
return (
<img class="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
)
}
function UserInfo(props) {
return (
<div class="UserInfo">
<Avatar user={props.user} />
<div class="UserInfo-name">
{props.user.name}
</div>
</div>
)
}
function Comment(props) {
return (
<div class="Comment">
<UserInfo user={props.author} />
<div class="Comment-text">
{props.text}
</div>
<div class="Comment-date">
{formatDate(props.date)}
</div>
</div>
)
}</code></pre>
<p>And here's the working example:</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="NwPrNJ" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-5" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/NwPrNJ/">Composi Tuts - Components and Props-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>If you wanted to, you could put the sub-components--Avatar and UserInfo--in separated files and import them into the Comment file. This would allow you to reuse them elsewhere in your project, or even reuse them in other projects.</p>
<h2>Props Should Be Read-Only</h2>
<p>Whether you create a functional or class-based component, it should never modify its own props. When functions don't modify their props, they are called pure. Take the following function as an example:</p>
<pre><code class="language-javascript">function sum(a, b) {
return a + b
}</code></pre>
<p>Given the same data, this function will always return the same result. This is a pure function because it does not change what its inputs are. In contrast, the following function is impure because it changes its inputs:</p>
<pre><code class="language-javascript">function withdraw(account, amount) {
account.total -= amount
}</code></pre>
<p>You should strive to make components act like pure functions with respect to their props. Of course, UI applications are dynamic and have user interactions. To handle these you can create stateful components. State allows components to change their output over time or due to user interaction without violating the pinciple of purity.</p>
<h2>The Component Class</h2>
<p>The Component class provides a useful API for makig more complex components than is possible with functional components. There are two ways to use the Component class: by creating an instance of it, or by extending it.</p>
<pre><code class="language-javascript">// Creating a new instance:
const title = new Comonent({})
// Extending the Component class:
class Title extends Component {
constuctor(props) {
super(props)
}
}</code></pre>
<p>Next we're going to look at extending Component. To learn about the component instance, refer to the <a href='../docs/component-instance.html' target="__blank">docs</a>.</p>
<p>As you can see above, to extend the Component class we usually need to include a constructor. This needs to have props passed all the way down to the Class <code>super</code>.</p>
<p>After that we need to tell the component where it should render. For that we use the <code>container</code> property. We also need to define a <code>render</code> funtion. Its purpose is to return the markup for the comonent.</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="KywMMO" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-6" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/KywMMO/">Composi Tuts - Components and Props-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>If you wanted to be able to render a component in different containers, you can leave it out and provide it as an argument in the component instantiation. You're free to take either approach: have all components rendered in the same container, or enable them to be rendered in different containers at initialization time.</p>
<p data-height="300" data-theme-id="6688" data-slug-hash="qVENaj" data-default-tab="js,result" data-user="rbiggs" data-embed-version="2" data-pen-title="Composi Tuts - Components and Props-7" class="codepen">See the Pen <a href="https://codepen.io/rbiggs/pen/qVENaj/">Composi Tuts - Components and Props-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> </p>
<h2>No Container</h2>
<p>It is possible to define a class component without providing a <code>container</code>. In such a case the component will be rendered in the body tag. If you will have no HTML shell in your document, you can use this technique to render your components. However, this is not a good practice. It means that when your document loads, the user may at first see nothing until the component renders. And if there is a JavaScript error, the user will get a blank page. Not good. It's therefore always best to provide a basic HTML shell into which you render you components. Always give your components a <code>container</code>.</p>
<h2>Querying the Component Tree</h2>
<p>Often when you are implementing complex components with events, you need to reach down into the child components to get a value from some input. You can simply the query by using the component's <code>element</code> property. This is the base element at the top of the markup hierarchy being returned by the component's <code>render</code> function. So, if a component return markup as follows:</p>
<pre><code class="language-javascript">render(data) {
return (
<div class='list__container'>
<p>
<button>Hello</button>
</p>
<ul>
{
data.map(item => <li>{item}</li>)
}
</ul>
</div>
)
}</code></pre>
<p>For the above component, <code>this.element</code> with be <code><div class='list__container'></div></code>. Using <code>this.element</code> as the starting point, we can limit our DOM query to the component base:</p>
<pre><code class="language-javascript">componentWasCreated() {
// Use "this.element" as base for event:
this.element.addEventListener('click', this)
}
handleEvent(e) {
e.target.nodeName === 'BUTTON' && this.sayHello()
}
sayHello() {
alert('Hello!')
}</code></pre>
<p>You could also do something like:</p>
<pre><code class="language-javascript">announce() {
// Use "this.element" as base for query:
const input = this.element.querySelector('input')
const value = input.value
if (value) {
this.setState(value)
}
}</code></pre>
<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 selected">
<a href='./components-n-props.html'>Components and Props</a>
</li>
<li class="tutorials__menu__item">
<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>