1- NodeJS
xxxxxxxxxx
11- VS Code
xxxxxxxxxx
21npx create-react-app helloworld
2cd helloworld
While there are a lot of files contained in here, just take a look at the package json and type the starting script:
xxxxxxxxxx
11npm start
And assuming you've got nothing else running on port 3000, go to: http://localhost:3000/
There's too much to start with. Let's delete everything:
xxxxxxxxxx
11rm src/*
And create a new landing page: index.js
xxxxxxxxxx
11touch src/index.js
Inside of index.js, type:
x1import React from "react";
2import ReactDOM from "react-dom";
3
4function HelloWorld() {
5 return (
6 <div>HELLO WORLD (or literally anything else you want to write here)</div>
7 );
8}
9
10ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
Inside the ReactDOM.render()
we've got a React element for the first parameter, and a DOM element for the second.
We have a component:
xxxxxxxxxx
11<HelloWorld />
This is a functional component (also known as a stateless function). There are class components, but those are deprecated.
xxxxxxxxxx
51class HelloWorld extends React.Component {
2 render() {
3 return <div>Hello World!</div>;
4 }
5}
Below the component, we have:
xxxxxxxxxx
11document.qeuerySelector("#root");
React uses the virtual dom. This creates a component hierarchy, renders those components, and then inserts them into the DOM where you tell the function. In our case, we're inserting our <HelloWorld/>
at #root
.
Remember our:
xxxxxxxxxx
51function HelloWorld() {
2 return (
3 <div>HELLO WORLD (or literally anything else you want to write here)</div>
4 );
5}
The in line HTML
looking code is our JSX
.
JSX is really a syntactic sugar for React's createElement()
:
xxxxxxxxxx
11React.createElement(component, props, children);
So, our Hello world function is:
xxxxxxxxxx
51React.createElement(
2 "div",
3 null,
4 "HELLO WORLD (or literally anything else you want to write here)"
5);
xxxxxxxxxx
111import React from "react";
2import ReactDOM from "react-dom";
3
4const HelloWorld = () =>
5 React.createElement(
6 "div",
7 null,
8 "HELLO WORLD (or literally anything else you want to write here)"
9 );
10
11ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
Notice that the children parameter looks like it has the spread operator. That meants that the component can have any number of children (remember the DOM).
xxxxxxxxxx
11React.createElement(component, props, children);
So we can write:
xxxxxxxxxx
131import React from "react";
2import ReactDOM from "react-dom";
3
4const HelloWorld = () =>
5 React.createElement(
6 "div",
7 null,
8 "HELLO WORLD",
9 "(or literally anything else",
10 " you want to write here)"
11 );
12
13ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
Just like HTML, you'll want to have components inside of components:
xxxxxxxxxx
171import React from "react";
2import ReactDOM from "react-dom";
3
4const GoodBye = () => {
5 return <span>Goodbye cruel world!</span>;
6};
7
8function HelloWorld() {
9 return (
10 <div>
11 HELLO WORLD (or literally anything else you want to write here)
12 <GoodBye />
13 </div>
14 );
15}
16
17ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
You may be wondering why we didn't just add <GoodBye\>
after the <div>HELLO WORLD (or literally anything else you want to write here)</div>
:
xxxxxxxxxx
151import React from "react";
2import ReactDOM from "react-dom";
3
4const GoodBye = () => {
5 return <span>Goodbye cruel world!</span>;
6};
7
8function HelloWorld() {
9 return (
10 <div>HELLO WORLD (or literally anything else you want to write here)</div>
11 <GoodBye />
12 );
13}
14
15ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
The error received was:
xxxxxxxxxx
101./src/index.js
2Line 11:5: Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <span>...</span>?
3
49 | return (
510 | <div>HELLO WORLD (or literally anything else you want to write here)</div>
6> 11 | <GoodBye />
7| ^
812 | );
913 | }
1014 |
When looking at this, it really makes not real sense at first glance. But let's try rewriting this with our React.createElement()
function:
xxxxxxxxxx
161import React from "react";
2import ReactDOM from "react-dom";
3
4const GoodBye = () =>
5 React.createElement("fragment", null, "Goodbye cruel world!");
6
7const HelloWorld = () => ({
8 React.createElement(
9 "div",
10 null,
11 "HELLO WORLD (or literally anything else you want to write here)"
12 )
13 <GoodBye />
14})
15
16ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
xxxxxxxxxx
141import React from "react";
2import ReactDOM from "react-dom";
3
4const HelloWorld = () => {
5 return React.createElement(
6 "div",
7 null,
8 "HELLO WORLD (or literally anything else you want to write here)"
9 );
10
11 React.createElement("fragment", null, "Goodbye cruel world!");
12};
13
14ReactDOM.render(<HelloWorld />, document.querySelector("#root"));
It may make a little more sense like:
xxxxxxxxxx
181import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Monde = () => {
9 return <span>Monde</span>;
10};
11
12const FrenchHelloWorld = () => {
13 return (
14 <Bonjour/>
15 <Monde />
16 )
17}
18 ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
xxxxxxxxxx
181import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Monde = () => {
9 return <span>Monde</span>;
10};
11
12const FrenchHelloWorld = () => {
13 return (
14 React.createElement(Bonjour, null)
15 React.createElement(Monde, null)
16 )
17}
18 ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
Javascript does not allow for you to pass back tuples, so you'll need to wrap all of your returned JSX in either a fragment, or some other tag.
xxxxxxxxxx
201import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Monde = () => {
9 return <span>Monde</span>;
10};
11
12const FrenchHelloWorld = () => {
13 return (
14 <div>
15 <Bonjour />
16 <Monde />
17 </div>
18 );
19};
20ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
The above code will render to the DOM as:
xxxxxxxxxx
61<div id="root">
2 <div>
3 <span>Bonjour</span>
4 <span>Monde</span>
5 </div>
6</div>
It's not always the case that you'll want to have your DOM be filled with a bunch of wrapper elements such as <div>
s. As of React 16.2, it's possible to use a fragment
which acts as a disappearing wrapper:
xxxxxxxxxx
201import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Monde = () => {
9 return <span>Monde</span>;
10};
11
12const FrenchHelloWorld = () => {
13 return (
14 <>
15 <Bonjour />
16 <Monde />
17 </>
18 );
19};
20ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
Now our DOM looks like:
xxxxxxxxxx
51<div id="root">
2 <span>Bonjour</span>
3 <span>Monde</span>
4</div>
5;
Sometimes you'll either want or need (many opinions about that need
) to run some javascript inside your JSX.
xxxxxxxxxx
181import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const name = "Jacques";
9
10const FrenchHelloWorld = () => {
11 return (
12 <>
13 <Bonjour />
14 name
15 </>
16 );
17};
18ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
You can access the javascript values by wrapping them within curly braces:
xxxxxxxxxx
181import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const name = " Jacques";
9
10const FrenchHelloWorld = () => {
11 return (
12 <>
13 <Bonjour />
14 {name}
15 </>
16 );
17};
18ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
Note: Whatever values you wrap within the curly braces must resolve to an actual value, otherwise, nothing will return:
xxxxxxxxxx
161import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const FrenchHelloWorld = () => {
9 return (
10 <>
11 <Bonjour />
12 {console.log("Wahtever")}
13 </>
14 );
15};
16ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
A good rule of thumb is: If you can assign it to a variable, then it's likely able to be rendered in JSX.
Suppose you wanted to have some material that displayed conditionally?
You can't write:
xxxxxxxxxx
261import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Hello = () => {
9 return <span>HELLO</span>;
10
11}
12
13const isFrench = true;
14
15const FrenchHelloWorld = () => {
16 return (
17 <>
18 { if(isFrench) {
19 <Bonjour />
20 } else {
21 <Hello />
22 }}
23 </>
24 );
25};
26ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
There are ways to get around this:
xxxxxxxxxx
171import React from "react";
2import ReactDOM from "react-dom";
3
4const Bonjour = () => {
5 return <span>Bonjour</span>;
6};
7
8const Hello = () => {
9 return <span>HELLO</span>;
10};
11
12const isFrench = true;
13
14const FrenchHelloWorld = () => {
15 return <>{isFrench ? <Bonjour /> : <Hello />}</>;
16};
17ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));
Another method of using conditionals within your application, such as a login
button is to make use of short circuiting
:
xxxxxxxxxx
141import React from "react";
2import ReactDOM from "react-dom";
3
4const isLoggedIn = false;
5const username = "reallyCoolGuy88";
6
7const LogInButton = () => (
8 <button onClick={() => alert("Log in, bozo")}> DO THE LOG IN PLEASE </button>
9);
10
11const DisplayLogin = () => {
12 return <> {isLoggedIn ? username : <LogInButton />} </>;
13};
14ReactDOM.render(<DisplayLogin />, document.querySelector("#root"));
When creating our own HTML
esque components, we need a way to pass data to them. HTML elements take in attributes. For React, we'll take in properties, or props
.
Props work just like a function parameter (in fact, on the component side of things, that's exactly what they are). When using JSX, however, you pass the data, just as if you're passing something to an attribute.
Unlike parameters in functions, props are read only and cannot be mutated. The flow of data always comes from the parent to the child.
xxxxxxxxxx
131import React from "react";
2import ReactDOM from "react-dom";
3
4const Hello = props => {
5 return <span>HELLO {props.name}</span>;
6};
7
8const SayHelloAndPassAProp = () => {
9 const somebodysName = "Matt";
10
11 return <Hello name={somebodysName} />;
12};
13ReactDOM.render(<SayHelloAndPassAProp />, document.querySelector("#root"));
Programatically, this allows for the ability to pass elements, both from the parent level to child elements:
xxxxxxxxxx
121const Hello = props => {
2 return <span>HELLO {props.name}</span>;
3};
4
5const isFrench = Math.random() >= 0.5;
6
7const SayHelloAndPassAProp = () => {
8 const somebodysName = "Matt";
9
10 return <Hello name={somebodysName} />;
11};
12ReactDOM.render(<SayHelloAndPassAProp />, document.querySelector("#root"));
xxxxxxxxxx
231import React from "react";
2import ReactDOM from "react-dom";
3
4const Hello = props => {
5 return <span>HELLO {props.name}</span>;
6};
7
8const Bonjour = ({ name }) => {
9 return <span> Bonjour {name} </span>;
10};
11
12const isFrench = Math.random() >= 0.5;
13
14const SayHelloAndPassAProp = () => {
15 const somebodysName = "Matt";
16
17 return isFrench ? (
18 <Bonjour name={somebodysName} />
19 ) : (
20 <Hello name={somebodysName} />
21 );
22};
23ReactDOM.render(<SayHelloAndPassAProp />, document.querySelector("#root"));
Taking a deeper look at props (via the React.createElement()
function):
xxxxxxxxxx
241import React from "react";
2import ReactDOM from "react-dom";
3
4const Hello = props => {
5 return React.createElement("span", null, "Hello " + props.name);
6};
7
8const Bonjour = ({ name }) => {
9 return React.createElement("span", null, "Bonjour " + name);
10};
11
12const isFrench = Math.random() >= 0.5;
13
14const SayHelloAndPassAProp = () => {
15 const somebodysName = "Matt";
16
17 return isFrench
18 ? React.createElement(Bonjour, { name: somebodysName }, null)
19 : React.createElement(Hello, { name: somebodysName }, null);
20};
21ReactDOM.render(
22 React.createElement(SayHelloAndPassAProp, null, null),
23 document.querySelector("#root")
24);
What's highly recommended is using JSX elements with props to keep your workspace clean:
xxxxxxxxxx
221import React from "react";
2import ReactDOM from "react-dom";
3
4const Hello = props => {
5 return <span>HELLO {props.name}</span>;
6};
7
8const Bonjour = ({ name }) => {
9 return <span> Bonjour {name} </span>;
10};
11
12const SomeGreeting = ({ languageBoolean, name }) =>
13 languageBoolean ? <Bonjour name={name} /> : <Hello name={name} />;
14
15const isFrench = Math.random() >= 0.5;
16
17const SayHelloAndPassAProp = () => {
18 const somebodysName = "Matt";
19
20 return <SomeGreeting name={somebodysName} languageBoolean={isFrench} />;
21};
22ReactDOM.render(<SayHelloAndPassAProp />, document.querySelector("#root"));
While dataflow with props is unidirectional, due to the nature of Javascript, there is a way for child elements to communicate with their parents via props (hint: think first class functions and scoping):
xxxxxxxxxx
271import React from "react";
2import ReactDOM from "react-dom";
3
4const Child = props => {
5 const num1 = 23;
6 const num2 = 42;
7 const sum = num1 + num2;
8
9 props.listen(`I CAN DO MATH! ${num1} + ${num2} is ${sum}`);
10
11 return <div> MATH IS FUN!</div>;
12};
13
14const Parent = () => {
15 const listenToTheChild = childWords => {
16 console.log(`Passed back a variable!!: `, childWords);
17 };
18
19 return (
20 <>
21 <div> It's good to learn math! </div>
22 <Child listen={listenToTheChild} />
23 </>
24 );
25};
26
27ReactDOM.render(<Parent />, document.querySelector("#root"));
It looked as if the above was immediate, let's slow things down with a button:
xxxxxxxxxx
241import React from "react";
2import ReactDOM from "react-dom";
3
4const Child = props => {
5 return <button onClick={props.listen}> Press me! (In the child) </button>;
6};
7
8const Parent = () => {
9 const listenToTheChild = eventResponseFromChild => {
10 console.log(
11 `Passed back a event from the button press!!: `,
12 eventResponseFromChild
13 );
14 };
15
16 return (
17 <>
18 <div> It's good to learn math! </div>
19 <Child listen={listenToTheChild} />
20 </>
21 );
22};
23
24ReactDOM.render(<Parent />, document.querySelector("#root"));
JSX supports nested components just like HTML, but how does that work? Let's take a look at the createElement
function again:
xxxxxxxxxx
11React.createElement(type, [props], [children]);
Notice that the children have been spread. That means that ultimately, we can have as many child elements as we please (but we'll start with just one, for now).
When the children are passed through the function, nothing is immediately done with them. For example:
xxxxxxxxxx
181import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = () => {
5 return <div>I AM SOME TEXT</div>;
6};
7
8const ComponentWithChildren = () => {
9 return (
10 <>
11 <TextComponent>
12 <span> I am a child of Text Component!!</span>
13 </TextComponent>
14 </>
15 );
16};
17
18ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
Because JSX elements are not the exact same things as HTML, we can't just write JSX like HTML without some extra steps.
To deal with child elements, we can treat them similarly to props:
xxxxxxxxxx
231import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = ({ children }) => {
5 return (
6 <>
7 {children}
8 <div>I AM SOME TEXT</div>
9 </>
10 );
11};
12
13const ComponentWithChildren = () => {
14 return (
15 <>
16 <TextComponent>
17 <span> I am a child of Text Component!!</span>
18 </TextComponent>
19 </>
20 );
21};
22
23ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
When dealing with multiple children, you can see that they're passed as an array:
xxxxxxxxxx
271import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = ({ children }) => {
5 console.log("Children: ", children);
6
7 return (
8 <>
9 {children}
10 <div>I AM SOME TEXT</div>
11 </>
12 );
13};
14
15const ComponentWithChildren = () => {
16 return (
17 <>
18 <TextComponent>
19 <div> Oh hi! I am another child! </div>
20 <span> I am a child of Text Component!!</span>
21 <span> I'm also a child </span>
22 </TextComponent>
23 </>
24 );
25};
26
27ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
You can access specific children by their element:
xxxxxxxxxx
281import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = ({ children }) => {
5 console.log("Children: ", children);
6
7 return (
8 <>
9 {children[0]}
10 {children[1]}
11 <div>I AM SOME TEXT</div>
12 </>
13 );
14};
15
16const ComponentWithChildren = () => {
17 return (
18 <>
19 <TextComponent>
20 <div> Oh hi! I am another child! </div>
21 <span> I am a child of Text Component!!</span>
22 <span> I'm also a child </span>
23 </TextComponent>
24 </>
25 );
26};
27
28ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
Children are JSX objects. If need be you can work with what's inside of them (though, in all actuality, you probably shouldn't, or should do it elsewhere, but it's good to know if you're in a pinch):
xxxxxxxxxx
291import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = ({ children }) => {
5 console.log("Children: ", children);
6
7 return (
8 <>
9 {React.Children.map(children, child => {
10 return child.props.children + "STUFF FROM INSIDE THE MAP";
11 })}
12 <div>I AM SOME TEXT</div>
13 </>
14 );
15};
16
17const ComponentWithChildren = () => {
18 return (
19 <>
20 <TextComponent>
21 <div> Oh hi! I am another child! </div>
22 <span> I am a child of Text Component!!</span>
23 <span> I'm also a child </span>
24 </TextComponent>
25 </>
26 );
27};
28
29ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
What happens if we pass in both props AND children? The solution is rather counterintuitive:
xxxxxxxxxx
261import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = props => {
5 return (
6 <>
7 <span> {props.whatever} </span>
8 {props.children}
9 <div>I AM SOME TEXT</div>
10 </>
11 );
12};
13
14const ComponentWithChildren = () => {
15 return (
16 <>
17 <TextComponent whatever="WORDS">
18 <div> Oh hi! I am another child! </div>
19 <span> I am a child of Text Component!!</span>
20 <span> I'm also a child </span>
21 </TextComponent>
22 </>
23 );
24};
25
26ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
In the TextComponent javascript, we're only passing in one single object.
xxxxxxxxxx
91const TextComponent = props => {
2 return (
3 <>
4 <span> {props.whatever} </span>
5 {props.children}
6 <div>I AM SOME TEXT</div>
7 </>
8 );
9};
So, ultimately, while we say "props", it's really just an object from which we can destructure any of our props, along with any of our children.
xxxxxxxxxx
91const TextComponent = ({ whatever, children }) => {
2 return (
3 <>
4 <span> {whatever} </span>
5 {children}
6 <div>I AM SOME TEXT</div>
7 </>
8 );
9};
When you're coding JSX, you'll almost inevitably forget to pass a prop at some given point. What ends up happening is that your prop becomes undefined:
xxxxxxxxxx
211import React from "react";
2import ReactDOM from "react-dom";
3
4const TextComponent = ({ whatever }) => {
5 return (
6 <>
7 <span> {whatever} </span>
8 <div>I AM SOME TEXT</div>
9 </>
10 );
11};
12
13const ComponentWithChildren = () => {
14 return (
15 <>
16 <TextComponent />
17 </>
18 );
19};
20
21ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
Here we forgot to pass whatever
as a prop, and ultimately it ended up undefined.
There are a number of ways to get around this, but the one that's build into react is PropTypes
. These enforce that we're pulling in the proper props!
Depending on what version of react you're using, you may need to install prop-types with (using create-react-app, you should be fine):
xxxxxxxxxx
11npm i --save prop-types
xxxxxxxxxx
261import React from "react";
2import ReactDOM from "react-dom";
3import PropTypes from "prop-types";
4
5const TextComponent = ({ whatever }) => {
6 return (
7 <>
8 <span> {whatever} </span>
9 <div>I AM SOME TEXT</div>
10 </>
11 );
12};
13
14TextComponent.propTypes = {
15 whatever: PropTypes.string.isRequired
16};
17
18const ComponentWithChildren = () => {
19 return (
20 <>
21 <TextComponent />
22 </>
23 );
24};
25
26ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));
The one thinig to note is that, while prop types will give you a warning in your developer console, it will only work if you remember to implement them.