-
Notifications
You must be signed in to change notification settings - Fork 39
/
react-intro.Rmd
797 lines (581 loc) · 51.1 KB
/
react-intro.Rmd
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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
# Introduction to React {#react}
This chapter introduces the [**React**](https://reactjs.org/) JavaScript library. React is "a JavaScript library for building user interfaces" developed by Facebook (though it is released as open-source software). At its core, React allows you to dynamically generate and interact with the DOM, similar to what you might do with jQuery. However, React was [created](https://reactjs.org/blog/2013/06/05/why-react.html) to make it much easier to define and manipulate lots of different parts of the DOM, and to do so _quickly_ (in terms of computer speed). It does this by enabling you to declare a web app in terms of different **components** (think: "things" or "parts" of a web page) that can be independently managed—this lets you design and implement web apps at a higher level of abstraction. Moreover, components are usually associated with some set of data, and React will automatically "re-render" (show) the updated component when the data changes—and to do so in a computationally efficient manner.
React is currently the most popular "framework" for building large-scale web applications (its chief competitors being [Angular](https://angular.io/) and [Vue.js](https://vuejs.org/), though there are [scores](https://javascriptreport.com/the-ultimate-guide-to-javascript-frameworks/) of similar frameworks that are also used. Check out [TodoMVC](http://todomvc.com/) for an example of the same application in each!). This means that React is generally very well documented; there are hundreds of tutorials, videos, and examples for building React applications (of which this chapter will be yet another). Some general resources are included at the end of the chapter, but in general we recommend you start with the [official documentation](https://reactjs.org/docs/getting-started.html), particularly the set of [main concepts](https://reactjs.org/docs/hello-world.html). Note that this book more or less follows the approach used by Facebooks's [Intro to React Tutorial](https://reactjs.org/tutorial/tutorial.html).
<p class="alert alert-warning">React has gone through a number of different major versions in its short life. What's more: the "style" in which React is written has also evolved over time as new features are added to both the React library and the JavaScript language. This book introduces and emphasizes an approach that prioritizes clarity of concepts and readability of code, rather than conciseness of syntax or use of advanced options, while also attempting to reflect the current practices in React development.</p>
<p class="alert alert-info">React is developed and maintained by Facebook (Meta). Facebook as a company has a long history of
[violating the Fair Housing Act](https://www.propublica.org/article/facebook-lets-advertisers-exclude-users-by-race),
[leaking or selling personal private data](https://techcrunch.com/2019/11/06/california-attorney-general-investigation-cambridge-analytica),
[allowing the spread of misinformation](https://www.cnn.com/2019/11/01/tech/facebook-false-ads-uk/index.html),
[enabling](https://www.theguardian.com/technology/2021/dec/06/rohingya-sue-facebook-myanmar-genocide-us-uk-legal-action-social-media-violence) [genocide](https://www.theguardian.com/technology/2022/feb/20/facebook-lets-vigilantes-in-ethiopia-incite-ethnic-killing)
and in general
[choosing profits over safety](https://www.nytimes.com/2021/10/03/technology/whistle-blower-facebook-frances-haugen.html). You may be understandably concerned about whether learning and using React implicitly supports and thus condones this company and its behavior. Whenever you adopt any software tool or library, it is worth investigating _who_ created it (and _why_) and how that may impact your own willingness to use it as a dependency.</p>
## Getting Set Up: React and Vite {#react-vite-setup}
React is a JavaScript library similar to those discussed in previous chapters—you load the library and then can call upon its methods. However, React makes use of a significant amount of advanced and custom JavaScript syntax called _JSX_ (described below), as well as extensive use of [ES6 Modules](#modules). Thus in practice, developing React apps requires using a number of different development tools—just getting setup and started is one of the major hurdles in learning React!
Most React apps are structured using a [production framework](https://react.dev/learn/start-a-new-react-project). Such frameworks collect and scaffold the different dependency libraries and build tools needed to start a React project. There are numerous such frameworks, each of which has its own opinions and restrictions on how React apps should be structured. For this course you'll use [**Vite**](https://vitejs.dev/) as a build tool framework. Vite (pronounced "veet") provides a quick a simple build environment that allows you to easily write "pure" React without needing to also learn additional framwork quirks. Once you have a foundational understanding of React, you can easily explore and learn additional frameworks and tools.
<p class="alert alert-warning">Facebook previously provided an official scaffolding tool called [Create React App](https://create-react-app.dev/), though that system has been abandoned (see e.g., [this developer comment](https://github.com/reactjs/react.dev/pull/5487#issuecomment-1409720741) for some context). Older versions of this textbook and course used Create React App, so you may still see some old references to it!</p>
Vite is a command line application that generates scaffolding ("starter code") for a React application, as well as provides built-in scripts to run, test, and deploy your code. You can use Vite to create a new React app by running the program, specifying name of the folder where you want the app to be created, and that Vite should scaffold a React application (Vite supports multiple different web frameworks). For example, the below command will create a new folder called `my-app` that contains all of the code and tools necessary to create and run a React app:
```bash
# Create a new React app project in a new `my-app` directory
# Hit 'y' when prompted!!
npm create vite@latest my-app -- --template react
# Change into new project directory to run further commands
cd my-app
# Install the build dependencies Vite has scaffolded
npm install
```
- The `npm create <package>` command is actually a shortcut for running `npx create-<package>`—you could equivalently write `npx create-vite@latest`, though the `create` command is more conventional. You do not need to install the `vite` (or `create-vite`) packages globally.
- If you specify the current directory (`.`) as the target folder name (where "my-app" is in the above example), Vite will create a new react app in the current folder! This is useful when you want to create a React app inside an existing cloned GitHub repository.
- The `--template react` argument specifies to use the React "starter" template. Vite supports multiple [community-defined templates](https://github.com/vitejs/awesome-vite#templates). The extra `--` before that argument helps `npm` know which named argument is being passed in.
### Running the Development Server {-}
After you create a React app, you can use Vite to start up a _development webserver_ by running the `dev` script:
```bash
# Make sure you are in the project directory
cd path/to/project
# Run the development server script
npm run dev
```
This will start up the webserver. You'll need to open up a browser to view the served content, by default at [http://localhost:5173/](http://localhost:5173/). This development webserver will automatically perform the following whenever you change the source code:
1. It will **transpile** React code into pure JavaScript that can be run in the web browser (see [_JSX_](#jsx) below).
2. It will manage and reference different JavaScript modules and dependencies, including external node modules.
2. It will display _errors and warnings_ in your browser window—most errors will show up in the developer console, while fatal errors (e.g., syntax errors that crash the whole app) will be shown as an error web page. Note that the [Chrome React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) will provide further error and debugging options.
3. It will automatically reload the page whenever you change the source code! (Though it is often good to manually refresh things when testing, just to make sure).
<p class="alert warning">In order to stop the server, hit `ctrl-c` on the command line.</p>
### Project Structure {-}
A newly created React app using the Vite `react` template will contain a number of different files, including the following:
- The `index.html` file is the home page for your application. If you look at this file, you can see that it contains almost no HTML content—just the `<head>`, the `<body>` and a single `<div id="root">`. That is because your entire app will be defined using React JavaScrip code; this content will then be injected into that `#root` element. _All of the _content_ of the page will be defined as JavaScript components—you'll be creating the entire page in JavaScript files. Thus in practice, you rarely modify the `index.html` file (beyond changing the metadata such as the `<title>` and favicon).
The `index.html` file also loads a script `/src/main.jsx`. The `.jsx` extension represents _JSX_ code (see below); Vite projects conventionally call this file `main.jsx` instead of `index.js`. All JavaScript files are found in the `src/` folder
The `index.html` file can also include `<link>` elements in the `<head>` in order to laod CSS. However, it is much preferred to instead import load any CSS content through _JavaScript_ code (though Vite will work with either approach). All CSS files are placed in the `src/` folder.
- The `src/` folder contains the source code for your React app. The app is started by the `main.jsx` script, but this script imports and uses other JavaScript modules (such as the provided `App.jsx` script, which defines the "App" module). In general, you'll code React apps by implementing components in one or more modules (e.g., `App.jsx`, `AboutPage.jsx`), which will then be imported and used by the `main.jsx` file. See the [_Components_]{#components} section below for details.
When starting a new React app, I recommend deleting _all_ of the content of `main.jsx` and `App.jsx` in order to start fresh.
- The `src/index.css` and `src/App.css` are both CSS files used to style the entire page and the individual "App" component respectively. You do not necessarily need both files; I recommend keeping just a single `index.css` file as you get staretd. Notice that the CSS files are imported from _inside the JavaScript files_ (with e.g., `import './index.css'` in the `src/main.jsx` file). This is because the Vite build system knows how to load CSS files, so you can "include" them through the JavaScript rather than needing to link them in the HTML.
If you want to load an external stylesheet, such as Bootstrap or Font-Awesome, you can still do that by modifying the `public/index.html` file—but it's better to install them as modules and import them through the JavaScript. See the documentation for [_Adding Bootstrap_](https://facebook.github.io/create-react-app/docs/adding-bootstrap) for an example.
- Remember that all CSS is global—rules specified in any `.css` file will apply to the entire app (i.e., the `App.css` rules will also apply to components other than those in `App.js`). The CSS is broken into multiple files to try and keep rules organized. You can instead put all your CSS rules into a single file, using naming schema or other techniques to keep them organized. If you are interested in ways to keep the CSS from being applied globally, look into using [CSS in JS](#css-in-js).
- The `src/assets` folder can be used to store media assets that are imported directly; I recommend deleting this and storing assets in the `public` folder instead. See [the Vite assets guide](https://vitejs.dev/guide/assets) for details.
- The `public/` folder is where you put assets that you want to be available to your page but aren't loaded and compiled through the JavaScript (i.e., they're not "source code"). For example, this is where you would put an `img/` folder to hold pictures to show. The `public/` folder be treated as the "root" of the webpage; a file found at `public/img/picture.jpg` would be referenced in your DOM as `img/picture.jpg`—using a path relative to the `public/` folder. See [the Vite assets guide](https://vitejs.dev/guide/assets) for details.
You can alternatively put assets into the `src/assets` folder, and then `import` them into the JavaScript (treating those images as "source code"). I don't recommend this when starting out; it's simpler to put images into the `public` folder and delete the `src/assets` folder.
- The two config files `eslint.config.json` and `vite.config.js` includes configuration information for eslint (for styling checks) and Vite respectively. You don't need to modify either of these, but do not delete them!
Overall, you'll mostly be working with the `.jsx` and `.css` files in the `src/` folder as you develop a React app.
## JSX
At its core, React is simply a DOM creation and rendering library—it allows you to declare new DOM elements (e.g., make a new `<h1>`) and then add that element to the webpage. This is similar to the process you might do with the DOM or the jQuery library:
```javascript
//Create and render a new `<h1>` element using DOM methods
const element = document.createElement('hi'); //create element
element.id = 'hello'; //specify attributes
element.classList.add('my-class');
element.textContent = 'Hello World!'; //specify content
document.getElementById('root').appendChild(element);
```
The React library provides a set of functions that do similar work:
```javascript
//Import required functions from the React libraries
import React from 'react';
import ReactDOM from 'react-dom/client';
//Create a new `<h1>` element using React methods. This function takes
//arguments indicating what the element should be
const msgElem = React.createElement(
//html tag
'h1',
//object of attributes
{ id: 'hello', className: 'myClass' },
//content
'Hello World!'
);
//Create a "React root" out of the `#root` elemment
const root = ReactDOM.createRoot(document.getElementById('root'));
//render the React element at that root
root.render(element)
```
React's `React.createElement()` lets you create an element (a "React element", not a pure DOM element—they are different!) by specifying the element type/tag, the content, and an object that maps from attribute names to values—all as a single statement. The **`ReactDOM.createRoot()`** function creates a context in which a React element can then be rendered (shown on the screen)—usually placing that in the `div#root` on the page. The root's `render()` method will then take a particular React element and insert it as the child content of the React root element . Note that `root.render()` function _replaces_ the content of the root DOM element—it's a "replace" not an "append" operation.
- Notice that the `class` attribute is specified with the **`className`** property. This is because `class` is a reserved keyword in JavaScript (it declares a [class](#es6-classes)).
<p class="alert alert-warning">The `createRoot()` function was introduced with React v18 (March 2022). Previous versions of React used the `ReactDOM.render()` function directly instead of explicitly creating a root context. Using `ReactDOM.render()` will cause your app to function as if using React v17, preventing some advanced features from working.</p>
The `React.createElement()` function is wordy and awkward to type, particularly if you'll be defining a lot of DOM elements (and in React, you define the entire application in JavaScript!). Thus React lets you create and define elements using [**JSX**](https://reactjs.org/docs/introducing-jsx.html). JSX is a _syntax extension_ to JavaScript: it provide a new way of writing the language—in particular, the ability to write bare HTML tags (finally!):
```jsx
//Create a new `<h1>` element using JSX syntax
const element = <h1 id="hello" className="myClass">Hello World</h1>;
//Create a "React root" out of the `#root` elemment
//then render the React element at that root
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element)
```
The value assigned to the `element` variable is _not_ a String (it has no quotes around it). Instead it is a _syntactical shortcut_ for calling the `React.createElement()` function—the HTML-like syntax can be automatically translated into the actual JavaScript for the method call, allowing you to write the simpler version.
- Notice that there is still a semicolon (`;`) at the end of the first line. You're not actually writing HTML, but defining a JavaScript value using syntax that mostly _looks_ like HTML!
- JSX can be used to define nested elements; you are not limited to a single element:
```jsx
//JSX can define nested elements
const header = (
<header className="banner">
<h1>Hello world!</h1>
</header>
);
```
It's very common to put elements on their own lines, intended as if you were writing HTML directly. If you do so, you'll need to surround the entire JSX expression in parentheses `()` to tell the interpreter that the line break shouldn't be read as implying a semicolon!
- Note that similar to XML, JSX syntax requires that all elements be closed; thus you need to use a closing slash at the end of empty elements:
```jsx
//JSX elements must be closed
const picture = <img src="my_picture.png" alt="a picture" />
```
Since JSX is not actually valid JavaScript (you can't normally include bare HTML), you need to "translate" it into real JavaScript so that the browser can understand and execute it. This process is called **transpiling**, and is usually performed using a _compiler_ program called [**Babel**](https://babeljs.io/). Babel is yet _another_ piece of the toolchain used to create React applications; however, all the details about transpiling with Babel are automatically handled by the Create React App scaffolding. "Transpile with Babel" is one of the steps performed by Webpack, which is included and run from the scripts provided by Create React App (whew!).
- Yes, chaining tools together like this is [how modern web development works](https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f).
In short: you write JSX code, and when you run your app with Create React App it will be turned into regular old JavaScript code that your browser can actually understand.
### Inline Expressions {-}
JSX allows you to include JavaScript expressions (such as variables you want to reference) directly inside of the HTML-like syntax. You do this by putting the expression inside of curly braces (**`{}`**):
```jsx
const message = "Hello world!";
const element = <h1>{message}</h1>;
```
When the element is rendered, the expression and the surrounding braces will be replaced by the value of the expression. So the above JSX would be rendered as the HTML: `<h1>Hello World</h1>`.
You can put any arbitrary expression inside of the `{}`—that is, any JavaScript code that resolves to a single value (think: anything that you could assign to a variable). This can include math operations, function calls, [ternary expressions](https://en.wikipedia.org/wiki/%3F:), or even an anonymous function values (see the next chapter for examples):
```jsx
//Including an inline expression in JSX. The expressions can be mixed directly
//into the HTML
const element = <p>A leap year has {(365 + 1) * 24 * 60} minutes!</p>;
```
You can use inline expressions most anywhere in JSX, including as the value of an attribute. You can even declare element attributes as an object and then use the _spread operator_ (`...`) to specify them as an inline expression.
```jsx
//Define a JavaScript variable
const imgUrl = 'path/to/my_picture.png';
//Assign that variable to an element attribute
const pic = <img src={imgUrl} alt="A picture" />;
//Define an object of element attributes
const attributes = {
src: 'path/to/other_picture.png',
alt: 'Another picture'
};
//A DOM element that includes those attributes, among others.
//The spread operator applies the property names as attributes of the element.
const otherPic = <img {...attributes} className="my-class" />;
```
Notice that when specifying a JavaScript expression as the value of an attribute (as with the `src` attribute of an `<img>`), you do not include quotes around the `{}`. The value of the inline expression is already a string, and it is that string that you're assigning to the attribute. Inline expressions aren't "copy-pasted" into the HTML-style of JSX; rather the values of those expressions are assigned to the object properties represented by the HTML-style attributes.
Importantly, the elements defined with JSX are _also_ valid JavaScript expressions! Thus you can use inline expressions to include one element inside of another.
```jsx
//Define variable content to display. One is a string, one is a React element.
const greetingString = "Good morning!";
const iconElem = <img src="sun.png" alt="A wide awake sun" />;
//Conditionally change values based on an `isEvening` value (defined elsewhere)
if(isEvening) {
greetingString = "Good evening!";
iconElem = <img src="moon.png" alt="A dozing moon" />;
}
//Include the variables inline in another React element
//Notice how the `icon` element is included as a child of the `<header>`
const header = (
<header>
<h1>{greetingString}</h1>
{iconElem}
</header>
);
```
If you include an _array_ of React elements in an inline expression, those those elements will be interpreted as all being children of the parent element (siblings of each other):
```jsx
//An array of React elements. Could also produce via a loop or function
const listItems = [<li>lions</li>, <li>tigers</li>, <li>bears</li>];
//Include the array as an inline expression; each `<li>` element will be
//included as a child of the `<ul>`
const list = (
<ul>
{listItems}
</ul>
);
```
The above sample would produce the HTML:
```html
<ul>
<li>lions</li>
<li>tigers</li>
<li>bears</li>
</ul>
```
This is particularly useful when using a variable number of elements, or generating elements directly from data (see the [_Props and Composition_](#props-and-composition) section below).
As a final note: you _cannot_ include HTML comments (`<!-- -->`) inside of JSX, as this isn't actually an element type. You also can't use inline JavaScript comments (`//`), because even multi-line JSX is still a single expression (that's why the `<header>` example above is written in parentheses). Thus in order to include a comment in JSX, you can use a _JavaScript block comment_ (`/* */`) inside of an inline expression:
```jsx
const main = (
<main>
{ /* A comment: the main part of the page goes here... */ }
</main>
)
```
## React Components
When creating React applications, you use JSX to define the HTML content of your webpage directly in the JavaScript. However, writing all of the HTML out as one giant JSX expression doesn't provide much advantage over just putting that content in `.html` files in the first place.
Instead, the React library encourages and enables you to describe your web page in terms of _components_, instead of just HTML elements. In this context, a **Component** is a "piece" of a web page. For example, in a social media application, each "status post" on a timeline might be a different Component, the "like" button may be its own Component, the "what are you thinking about?" form would be a Component, the page navigation would be a Component, and so forth. Thus you can almost think of a Component as a semantic abstraction of a `<div>` element—a "chunk" of the page. But like with `<div>`s, a Component can be seen as made of of _other_ Components—you might have a "Timeline" Component that is made up of many "StatusPost" Components, and each StatusPost Component may include a "ReactionButton" Component.
Thinking about a page in terms of Components instead of HTML elements can make it much easier to design and reason about your page. You don't need to worry about the code syntax or the specific elements to use, you can just think of your page in terms of its overall layout and content—what needs to be on the page, and how that content is organized—the same way you might mock up a web page in a graphic design program. See the documentation article [_Thinking in React_](https://beta.reactjs.org/learn/thinking-in-react) for an example of how to design a page in terms of Components.
React enables you to _implement_ your page in terms of [explicit Components](https://reactjs.org/docs/components-and-props.html) by providing a way for you to define your own XML elements! Thus you can define a `<Timeline>` element, which has multiple `<StatusPost>` elements as children. Components will be able to be included directly in your JSX, made up of and mixed in with standard HTML elements.
<div class="row">
<div class="column col2">
![An example page broken up into components. Example from <http://coenraets.org/blog/2014/12/sample-mobile-application-with-react-and-cordova/>](img/react/uimockscript.png)
</div>
<div class="column col2" style="font-size:14px">
```xml
<App>
<HomePage>
<Header />
<SearchBar />
<EmployeeList>
<EmployeeListItem person="James King" />
<EmployeeListItem person="Julie Taylor" />
<EmployeeListItem person="Eugene Lee" />
</EmployList>
</HomePage>
<EmployeePage>
<Header />
...
</EmployeePage>
</App>
```
</div>
</div>
React components are most commonly defined using **function components**: JavaScript functions that _return_ the DOM elements (the JSX) that will be rendered on the screen to show that Component:
```jsx
//Define a Component representing information about a user
function UserInfoCard(props) {
//This is an everyday function; you can include any code you want here
//including variables, if statements, loops, etc
const name = "Ethel";
const descriptor = "Aardvark";
//Return a React element (JSX) that is how the Component will appear
return (
<div>
<h1>{name}</h1>
<p>Hello, my name is {name} and I am a {descriptor}</p>
</div>
)
}
//Create a new value (a React element)
//This syntax in effect "calls" the function
const infoElement = <UserInfoCard />;
//Show the element in the web page (inside #root)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(infoElement);
```
This function defines a new Component (think: a new HTML element!) called `UserInfoCard`. Things to note about defining this component:
1. A Component function **must** be named starting with a Capital letter (and usually in CamelCase). This is to distinguish a custom component from a "normal" React element.
- If you see a function with a Capitalized name, know that it's actually a defining a Component!
- It's also possible for function components to be defined as anonymous arrow functions assigned to `const` variables:
```jsx
const Example = (props) => {
return <div>...</div>
}
```
This provides some benefits in terms of scoping (because of the arrow function), but otherwise makes Components even harder to read—its not as readily obviously that this is a function and a Component. You should stick to defining component functions using the `function` keyword (and not arrow functions) for readability, but be aware of the alternate approach which is used in some libraries and examples.
2. Component functions take in a single argument representing the [props](#props) (discussed below). As such by convention it is **always** named **`props`** (with an **s**!). While it's possible to omit this argument if it isn't used (as you can with all JavaScript functions), best practice is to always include it.
3. A component function is a regular function like any other that you've written. That means that it can and often does do more than just return a single value! You can include additional code statements (variable declarations, if statements, loops, etc) in order to calculate the values you'd like to include in inline expressions in the returned JSX.
<div class="alert alert-warning">**Style requirement**: perform any complex logic—including functional looping such as with `map()` or `filter()`—outside of the `return` statement. Keep the `return` statement as simple as possible so it remains readable—and more importantly _debuggable_ (so you can use things like `console.log` statements to check intermediate values before they are returned). **NEVER** include an inline callback function so that you have a `return` statement inside of a `return` statement!</div>
4. A Component function can only return a _single_ React element (you can't return more than one value at a time from a JavaScript function). However, that single React element can have as many children as you wish. It's very common to "wrap" all of the content you want a component to include in a single `<div>` to return, as in the above example.
If you don't like want to add an extra div for some reason, it's also possible to "wrap" content in a [Fragment](https://reactjs.org/docs/fragments.html), which can be declared using a shortcut syntax that looks like an element with no name:
```jsx
function FragmentExample(props) {
return (
<>
<p>First paragraph</p>
<p>Second paragraph</p>
</>
)
}
```
Once it has been defined, a Component can then be created in JSX using XML-style syntax, just like any other element (i.e., as `<UserInfoCard />`). In effect, this "calls" the function component in order to create it—you never call the function directly. You can use the created Component like any other React element, such as including it as a child of other HTML (e.g., nest it inside of a `<div>`). A Component thus _encapsulates_ a chunk of HTML, and you can almost think of it as a "shortcut" for including that HTML.
<p class="alert alert-warning">**Never** call a component function as a function with `MyComponent()`! **Always** render as an element with `<MyComponent/>`.</p>
Because Components are instantiated as React elements, that means that you can use them anywhere you would use a normal HTML element—including in the returned value of another component! This is called _composing_ components—you are specifying that one component is "composed" (made up of) others:
```jsx
//A component representing a message
function HelloMessage(props) {
return <p>Hello World!</p>;
}
//A component representing another message
function GoodbyeMessage(props) {
return <h1>See ya later!</h1>;
}
//A component that is composed of (renders) other components!
function MessageList(props) {
return (
<div>
<h1>Messages for you</h1>
<HelloMessage /> {/* include a HelloMessage component */}
<GoodbyeMessage /> {/* include a GoodbyeMessage component */}
</div>
);
}
//Render an instance of the "top-level" ("outer") component
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList />);
```
In the above example, the `MessageList` Component is composed of (renders) instances of the `HelloMessage` and `GoodbyeMessage` Components; when the `MessageList` is displayed, the interpreter will create the two other components, rendering each in turn. In the end, all of the content displayed will be standard HTML elements—they've just been organized into Components.
- Components are usually instantiated as empty elements, though it is possible to have them [include child elements](https://reactjs.org/docs/composition-vs-inheritance.html#containment).
- Notice that you can and do mix standard HTML elements and Components together; Components are just providing a useful abstraction for organizing content, logic, and data!
In practice, React applications are made up of many different Components—dozens or even hundreds depending on the size of the app! The "top-most" component is commonly called **`App`** (since it represents the whole "app"), with the `App`'s returned value instantiating further components, each of which represents a different part of the page.
The `App` component will represent the entirety of the website—all of your app's content will be defined in React components (which will be rendered as children of the `<App>`). But since the React components will be inserted inside of the `<div id="root">` element that is already in the `index.html` file, you _do not_ ever include `<body>` or `<head>` elements in your React components—those elements are already defined in the HTML file itself, so don't get created by React!
See [_Props and Composition_](#props-composition) below for examples of common ways that React components are composed.
#### Strict Mode {-}
If you look at the starter code generated by Vite, you will see a common pattern of "wrapping" a rendered `<App>` component in a `<React.StrictMode>` component:
```jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
```
The [**`StrictMode`**](https://reactjs.org/docs/strict-mode.html) component is provided by React (it's a property of the `React` object imported in the first line of the above example). Kind of like the `use strict` declaration for JavaScript, this component enables a number of additional error checking that React can perform— though most of the issues it identifies are due to using out-of-date practices or errors with features not covered in this course. It can be useful, though it will cause duplicated output of some `console.log()` statements and is not strictly necessary.
### Component Organization {-}
React applications will often have dozen of components, quickly growing more complex than the basic starting examples in the previous section. If you tried to define all of these components in a single `index.js` file, your code would quickly become unmanageable.
Thus individual components are usually defined inside of separate [**modules**](#modules) (files), and then _imported_ by the modules that need them—other components or the root `index.js` file.
Recall that in order to make a variable or function (e.g., a Component) available to other modules, you will need to `export` that component from its own module, and then `import` that value elsewhere:
```jsx
/* in Messages.js file */
//Export the defined components (as named exports)
export function HelloMessage() { /* ... */ }
export function GoodByeMessage() { /* ... */ }
```
```jsx
/* in App.js file */
//Import components needed from other modules
import { HelloMessage, GoodbyeMessage } from `./Messages.js`; //named imports!
//Can also export as a _default_ export; common if the file has only one component
export default function App() {
return (
<div>
{/* Use the imported components */}
<HelloMessage />
<GoodbyeMessage />
</div>
)
}
```
```jsx
/* in index.js file */
//Import components needed from other modules
import App from `./App.js`; //default import!
//Render the imported component
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
```
This structure allows you to have each component (or set of related components) in a different file, helping to organize your code.
- This "export-import" structure implies a _one-directional_ hierarchy: the `Message` components don't know about the `App` component that will render them (they are self-contained). You should not have two files that import from each other!
- The `index.js` file will usually just import a single component; that file stays very short and sweet. Put all of your component definitions in other files.
It is common to use the file system to further organize your component modules, such as grouping them into different folders, as in the below example:
```
src/
|-- components/
|-- App.js
|-- navigation/
|-- NavBar.js
|-- utils/
|-- Alerts.js
|-- Buttons.js
index.js
```
Note that a module such as `Alerts.js` might define a number of different "Alert" components to use; these could be imported into the `NavBar.js` or `App.js` modules as needed. The `App.js` component would be imported by the `index.js` file, which would contain the call to `root.render()`.
However, it is not necessary to put each different component in a separate module; think about what kind of file organize will make it easy to find your code and help others to understand how your app is structured!
### Deprecated Alternative: Class Components {-}
In order versions and styles of React, it was alternatively possible to define components as **classes** (using [ES6 Class syntax](#es6-classes)) that acts as a template or blueprint for that content. In particular, you define a class that _inherits_ from the [**`React.Component`**](https://reactjs.org/docs/react-component.html) class—this inheritance means that the class you define _IS A_ React Component!
```jsx
//Define a class component representing information about a user
class UserInfo extends React.Component {
//Components MUST override the `render()` function, which must return a
//DOM element
render() {
//This is an everyday function; you can include any code you want here
const name = "Ethel";
const descriptor = "Aardvark";
//Return a React element (JSX) that is how the component will appear
return (
<div>
<h1>{name}</h1>
<p>Hello, my name is {name} and I am a {descriptor}</p>
</div>
)
}
}
//instantiate the class as a new value (a React element)
const infoElement = <UserInfo />;
//Show the element in the web page (inside #root)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(infoElement);
```
This "class component" example is identical to the previous function component example—it takes the "body" of the function component and moves that into a (required) `render()` method.
- Note that the `React.Component` class is often imported as a _named import_, so that you can refer to it directly:
```jsx
import {Component} from 'react';
class MyComponent extends Component {
render() {
//...
}
}
```
Current best practices in React are to **only** use function components and to **not** use class components; class components provide no extra functionality, and can introduce some additional complexities (particularly for handling their _state_). However it is good to be aware of the option in case you run across it, and thinking about components as "classes" can be useful for understanding how they are defined and used.
<div class="alert alert-info">Additionally, older versions of React included a function `React.createClass()` that took in an _object_ whose properties were functions that would act as class methods (see [here](https://toddmotto.com/react-create-class-versus-component/) for details). Although this function has been removed, it's worth being aware of in case you come across older examples of React code that you need to interpret or adapt.</div>
## Properties (`props`) {#props}
One of the main goals of using a library such as React is to support _data-driven views_—that is, the content that is rendered on the page can be based on some underlying data, providing a visual representation of that data. And if that underlying data changes, React can automatically and efficiently "re-render" the view, making the page dynamic.
You can specify the underlying data that a React component will be representing by specifying [**properties**](https://reactjs.org/docs/components-and-props.html) (usually just called **props**) for that component. Props are the "input parameters" to a component—they are the details you _pass_ to it so that it knows what and how to render its content.
You pass a prop to a component when you instantiate it by specifying them as XML attributes (using `name=value` syntax)—the props are the "attributes" of the component!
```jsx
//Passing a prop called `message` with value "Hello property"
const messageA = <MessageItem message="Hello property!" />;
//Passing a prop using an inline expression
const secret = "Shave and a haircut";
const messageB = <MessageItem message={secret} />;
//A component can accept multiple props
//This component takes in a `userName` prop as well as a `descriptor` prop
const userInfo = <UserInfo userName="Ethel" descriptor="Aardvark" />;
```
- Prop names must be valid JavaScript identifiers, and so should be written in camelCase (like variable names).
<p class="alert alert-info">Components are usually defined so that they expect a certain set of props—similar to how a function is defined to expect a certain set of arguments. It is possible to pass "extra" props to the Component (by specifying additional XML attributes), but if the component doesn't expect those props it won't do anything with them. Similarly, failing to pass an expected prop will likely result in errors—just like if you didn't pass a function an expected argument!</p>
For function components, the `props` are received as a single argument to the function (usually called `props`). This argument is a regular JavaScript _object_ whose keys are the props' "names":
```jsx
//Define a component representing information about a user
function UserInfo(props) {
console.log(props) //you can see this is an object!
//access the individual props from inside the argument object
const userName = props.userName;
const descriptor = props.descriptor;
//can use props for logic or processing
const userNameUpper = props.userName.toUpperCase();
return (
<div>
<h1>{userNameUpper}</h1>
<p>Hello, my name is {userName} and I am a {descriptor}</p>
</div>
)
}
const userInfo = <UserInfo userName="Ethel" descriptor="Aardvark" />;
```
Again, _all_ props are stored in the argument object—you have to access them through that value.
- To avoid having to type `props.___` all the time, it's common to use [object destructuring](#destructuring) to assign all of the props to variables in a single line:
```jsx
function UserInfo(props) {
//assign the prop object properties to variables
const {userName, descriptor} = props;
return <h1>{userName}</h1>;
}
```
It's also possible to specify the object destructuring in the argument declaration!
```jsx
//The props object passed in will be destructured into two argument variables
function UserInfo({userName, descriptor}) {
return <h1>{userName}</h1>;
}
```
This can help make it clear in the source code what props a component expects, though can make the code somewhat more difficult to parse (since there are so many parentheses and curly braces). However, to keep the idea of the `props` clear, all examples in this book will use a single, non-destructured `props` object.
Importantly, props can be **any** kind of value! This includes basic types such as Numbers or Strings, data structures such as Arrays or Objects, or even functions or other component instances!
```jsx
//Props can be of any data type!
//Pass an array as a prop!
const array = [1,2,3,4,5];
const suitcase = <Suitcase luggageCombo={array} />;
//Pass a function as a prop (like a callback)!
function sayHello() {
console.log('Hello world!');
}
const greeting = <Greeting callback={sayHello} />;
//Pass another Component as a prop (not common)!
const card = <HolidayCard message="Greetings world!" />
const gift = <Gift toMsg="Ethel", fromMsg={card} />
```
Indeed, passing _callback functions_ as props to other components is a major step in designing interactive React applications. See the next chapter for more details and examples.
### Props and Composition {-}
<!-- > "props are a Component's configuration, its options if you may. They are received from above and immutable as far as the Component receiving them is concerned. A Component cannot change its props, but it is responsible for putting together the props of its child Components." - [props vs. state](https://github.com/uberVU/react-guide/blob/master/props-vs-state.md) -->
Recall that React components are usually _composed_, with one component's returned value including instances of other components (as in the `MessageList` example above). Since props are specified when a component instance is created, this means that a "parent" component will need to specify the props for its children ("passing the props"). Commonly, the props for the child components will be derived from the props of the parent component:
```jsx
//Defines a component representing a message in a list
function MessageItem(props) {
return <li>{props.message}</li>
}
//A component that renders a trio of messages
function MessageListTrio(props) {
const messages = props.messages; //assign prop to a local variable for convenience
return (
<div>
<h1>Messages for you</h1>
<ul>
{/* instantiate child components, passing data from own props */}
<MessageItem message={messages[0]} />
<MessageItem message={messages[1]} />
<MessageItem message={messages[2]} />
</ul>
</div>
);
}
//Define and pass a prop to the parent comment when rendering it
const messagesArray = ["Hello world", "No borders", "Go huskies!"];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageListTrio messages={messagesArray});
```
This creates a **one-directional data flow**—the data values are passed into the parent, which then passes data down to the children (not vice versa).
The component organization in the previous example is [particularly common](https://reactjs.org/docs/lists-and-keys.html) in React. In order to effectively render a list (array) of data, you should define a Component that declares how to render a _single_ element of that list (e.g., `MessageItem`). This allows you to focus on only a single problem, developing a re-usable solution. Then you define another component that represents how to render a whole list of elements (e.g., `MessageList`). This component will render a child item component for each element—it just needs to set up the list, without worrying about what each item looks like!
The data set can be passed into the parent (List) component as a prop, and each individual element from that data set will be passed into an instance of the child (Item) component. In effect, the List component is tasked with taking the data set (an array of data values) and producing a set Item components (an array of Components). This kind of transformation is a _mapping_ operation (each data value is mapped to a Component), and so can be done effecttively with the **`map()`** method:
```jsx
//Map an array of message strings to an array of components
const msgItems = props.messages.map((msgString) => {
const component = <MessageItem message={msgString} />; //pass prop down!
return component; //add this new component to resulting array
})
```
And because including an _array of Components_ as an inline expression in JSX will render those elements as siblings, it's easy to render this list from within the parent:
```jsx
function MessageList(props) {
//the data: an array of strings ["", ""]
const messageStringArray = props.messages;
//the renderable content: an array of elements [<>, <>]
const msgElemArray = messageStringArray.map((msgString) => {
const component = <MessageItem message={msgString} />; //pass prop down!
return component; //add this new component to resulting array
})
return (
<div>
<h1>Messages for you</h1>
<ul>
{/* render the array of MessageList components */}
{msgElemArray}
</ul>
</div>
);
}
//Define and pass a prop to the parent comment when rendering it
const messagesArray = ["Hello world", "No borders", "Go huskies!"];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList messages={messagesArray});
```
Note that the above example will issue a warning in the developer console. This is because React requires you to specify an additional prop called [**`key`**](https://reactjs.org/docs/lists-and-keys.html#keys) for each element in an array of components. The `key` should be a _unique string_ for each element in the list, and acts as an identifier for that element (think: what you might give it as an `id` attribute). React will use the `key` to keep track of those elements, so if they change over time (i.e., elements are added or removed from the array) React will be able to more efficiently modify the DOM.
If unspecified, the `key` will default to the element's index in the array, but this is considered unstable (since it will change as items are added or removed). Thus a better approach is to determine a unique value based on the data—which means that data items will need to be uniquely identifiable. The below improved version of the code changes each message from a basic String to an Object that has `content` and `id` properties, allowing each data value to be uniquely identified. Note that the `MessageItem` component need not be changed; it still renders an `<li>` showing the given `message` prop!
```jsx
const MESSAGE_DATA = [
{content: "Hello world", id: 1}
{content: "No borders", id: 2}
{content: "Go huskies!", id: 3}
];
function MessageList(props) {
//the data: an array of objects [{}, {}]
const messageObjArray = props.messages;
//the renderable content: an array of elements [<>, <>]
const msgElemArray = messageObjArray.map((msgObject) => {
//return a new MessageItem for each message object
//attributes are listed on their own lines for readability
return <MessageItem
message={msgObject.content}
key={msgObject.id.toString()} {/* pass in a key prop! */}
/>;
}) //end of .map()
return (
<div>
<h1>Messages for you</h1>
<ul>
{msgElemArray}
</ul>
</div>
); //end of return
}
//Define and pass a prop to the parent comment when rendering it
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList messages={MESSAGE_DATA});
```
This pattern enables you to effectively define your web page's content in terms of individual components that can be composed together, allowing you to quickly render large amounts of data (without needing spend a lot of time thinking about loops!).
## Resources {-}
As mentioned above, there are a large number of different resources for learning React. Below are links to key parts of the official documentation.
<div class="list-condensed">
- [Thinking in React](https://facebook.github.io/react/docs/thinking-in-react.html)
- [Facebook's React Tutorial](https://facebook.github.io/react/tutorial/tutorial.html)
- [React Documentation](https://facebook.github.io/react/docs/hello-world.html)
- [JSX Documentation](https://facebook.github.io/react/docs/introducing-jsx.html)
- [Components and Props](https://reactjs.org/docs/components-and-props.html)
- [React API References](https://reactjs.org/docs/react-api.html)
</div>