1- NodeJS
xxxxxxxxxx11- VS Code
xxxxxxxxxx21npx create-react-app helloworld2cd helloworldWhile there are a lot of files contained in here, just take a look at the package json and type the starting script:
xxxxxxxxxx11npm startAnd 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:
xxxxxxxxxx11rm src/*And create a new landing page: index.js
xxxxxxxxxx11touch src/index.jsInside of index.js, type:
x1import React from "react";2import ReactDOM from "react-dom";34function HelloWorld() {5 return (6 <div>HELLO WORLD (or literally anything else you want to write here)</div>7 );8}910ReactDOM.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:
xxxxxxxxxx11<HelloWorld />This is a functional component (also known as a stateless function). There are class components, but those are deprecated.
xxxxxxxxxx51class HelloWorld extends React.Component {2 render() {3 return <div>Hello World!</div>;4 }5}Below the component, we have:
xxxxxxxxxx11document.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:
xxxxxxxxxx51function 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():
xxxxxxxxxx11React.createElement(component, props, children);So, our Hello world function is:
xxxxxxxxxx51React.createElement(2 "div",3 null,4 "HELLO WORLD (or literally anything else you want to write here)"5);xxxxxxxxxx111import React from "react";2import ReactDOM from "react-dom";34const HelloWorld = () =>5 React.createElement(6 "div",7 null,8 "HELLO WORLD (or literally anything else you want to write here)"9 );1011ReactDOM.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).
xxxxxxxxxx11React.createElement(component, props, children);So we can write:
xxxxxxxxxx131import React from "react";2import ReactDOM from "react-dom";34const HelloWorld = () =>5 React.createElement(6 "div",7 null,8 "HELLO WORLD",9 "(or literally anything else",10 " you want to write here)"11 );1213ReactDOM.render(<HelloWorld />, document.querySelector("#root"));Just like HTML, you'll want to have components inside of components:
xxxxxxxxxx171import React from "react";2import ReactDOM from "react-dom";34const GoodBye = () => {5 return <span>Goodbye cruel world!</span>;6};78function HelloWorld() {9 return (10 <div>11 HELLO WORLD (or literally anything else you want to write here)12 <GoodBye />13 </div>14 );15}1617ReactDOM.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>:
xxxxxxxxxx151import React from "react";2import ReactDOM from "react-dom";34const GoodBye = () => {5 return <span>Goodbye cruel world!</span>;6};78function HelloWorld() {9 return (10 <div>HELLO WORLD (or literally anything else you want to write here)</div>11 <GoodBye />12 );13}1415ReactDOM.render(<HelloWorld />, document.querySelector("#root"));The error received was:
xxxxxxxxxx101./src/index.js2Line 11:5: Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <span>...</span>?349 | 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:
xxxxxxxxxx161import React from "react";2import ReactDOM from "react-dom";34const GoodBye = () =>5 React.createElement("fragment", null, "Goodbye cruel world!");67const HelloWorld = () => ({8 React.createElement(9 "div",10 null,11 "HELLO WORLD (or literally anything else you want to write here)"12 )13 <GoodBye />14})1516ReactDOM.render(<HelloWorld />, document.querySelector("#root"));xxxxxxxxxx141import React from "react";2import ReactDOM from "react-dom";34const HelloWorld = () => {5 return React.createElement(6 "div",7 null,8 "HELLO WORLD (or literally anything else you want to write here)"9 );1011 React.createElement("fragment", null, "Goodbye cruel world!");12};1314ReactDOM.render(<HelloWorld />, document.querySelector("#root"));It may make a little more sense like:
xxxxxxxxxx181import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Monde = () => {9 return <span>Monde</span>;10};1112const FrenchHelloWorld = () => {13 return (14 <Bonjour/>15 <Monde />16 )17}18 ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));xxxxxxxxxx181import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Monde = () => {9 return <span>Monde</span>;10};1112const 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.
xxxxxxxxxx201import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Monde = () => {9 return <span>Monde</span>;10};1112const 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:
xxxxxxxxxx61<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:
xxxxxxxxxx201import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Monde = () => {9 return <span>Monde</span>;10};1112const FrenchHelloWorld = () => {13 return (14 <>15 <Bonjour />16 <Monde />17 </>18 );19};20ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));Now our DOM looks like:
xxxxxxxxxx51<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.
xxxxxxxxxx181import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const name = "Jacques";910const FrenchHelloWorld = () => {11 return (12 <>13 <Bonjour />14 name15 </>16 );17};18ReactDOM.render(<FrenchHelloWorld />, document.querySelector("#root"));You can access the javascript values by wrapping them within curly braces:
xxxxxxxxxx181import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const name = " Jacques";910const 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:
xxxxxxxxxx161import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const 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:
xxxxxxxxxx261import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Hello = () => {9 return <span>HELLO</span>;1011}1213const isFrench = true;1415const 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:
xxxxxxxxxx171import React from "react";2import ReactDOM from "react-dom";34const Bonjour = () => {5 return <span>Bonjour</span>;6};78const Hello = () => {9 return <span>HELLO</span>;10};1112const isFrench = true;1314const 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:
xxxxxxxxxx141import React from "react";2import ReactDOM from "react-dom";34const isLoggedIn = false;5const username = "reallyCoolGuy88";67const LogInButton = () => (8 <button onClick={() => alert("Log in, bozo")}> DO THE LOG IN PLEASE </button>9);1011const DisplayLogin = () => {12 return <> {isLoggedIn ? username : <LogInButton />} </>;13};14ReactDOM.render(<DisplayLogin />, document.querySelector("#root"));When creating our own HTMLesque 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.
xxxxxxxxxx131import React from "react";2import ReactDOM from "react-dom";34const Hello = props => {5 return <span>HELLO {props.name}</span>;6};78const SayHelloAndPassAProp = () => {9 const somebodysName = "Matt";1011 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:
xxxxxxxxxx121const Hello = props => {2 return <span>HELLO {props.name}</span>;3};45const isFrench = Math.random() >= 0.5;67const SayHelloAndPassAProp = () => {8 const somebodysName = "Matt";910 return <Hello name={somebodysName} />;11};12ReactDOM.render(<SayHelloAndPassAProp />, document.querySelector("#root"));xxxxxxxxxx231import React from "react";2import ReactDOM from "react-dom";34const Hello = props => {5 return <span>HELLO {props.name}</span>;6};78const Bonjour = ({ name }) => {9 return <span> Bonjour {name} </span>;10};1112const isFrench = Math.random() >= 0.5;1314const SayHelloAndPassAProp = () => {15 const somebodysName = "Matt";1617 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):
xxxxxxxxxx241import React from "react";2import ReactDOM from "react-dom";34const Hello = props => {5 return React.createElement("span", null, "Hello " + props.name);6};78const Bonjour = ({ name }) => {9 return React.createElement("span", null, "Bonjour " + name);10};1112const isFrench = Math.random() >= 0.5;1314const SayHelloAndPassAProp = () => {15 const somebodysName = "Matt";1617 return isFrench18 ? 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:
xxxxxxxxxx221import React from "react";2import ReactDOM from "react-dom";34const Hello = props => {5 return <span>HELLO {props.name}</span>;6};78const Bonjour = ({ name }) => {9 return <span> Bonjour {name} </span>;10};1112const SomeGreeting = ({ languageBoolean, name }) =>13 languageBoolean ? <Bonjour name={name} /> : <Hello name={name} />;1415const isFrench = Math.random() >= 0.5;1617const SayHelloAndPassAProp = () => {18 const somebodysName = "Matt";1920 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):
xxxxxxxxxx271import React from "react";2import ReactDOM from "react-dom";34const Child = props => {5 const num1 = 23;6 const num2 = 42;7 const sum = num1 + num2;89 props.listen(`I CAN DO MATH! ${num1} + ${num2} is ${sum}`);1011 return <div> MATH IS FUN!</div>;12};1314const Parent = () => {15 const listenToTheChild = childWords => {16 console.log(`Passed back a variable!!: `, childWords);17 };1819 return (20 <>21 <div> It's good to learn math! </div>22 <Child listen={listenToTheChild} />23 </>24 );25};2627ReactDOM.render(<Parent />, document.querySelector("#root"));It looked as if the above was immediate, let's slow things down with a button:
xxxxxxxxxx241import React from "react";2import ReactDOM from "react-dom";34const Child = props => {5 return <button onClick={props.listen}> Press me! (In the child) </button>;6};78const Parent = () => {9 const listenToTheChild = eventResponseFromChild => {10 console.log(11 `Passed back a event from the button press!!: `,12 eventResponseFromChild13 );14 };1516 return (17 <>18 <div> It's good to learn math! </div>19 <Child listen={listenToTheChild} />20 </>21 );22};2324ReactDOM.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:
xxxxxxxxxx11React.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:
xxxxxxxxxx181import React from "react";2import ReactDOM from "react-dom";34const TextComponent = () => {5 return <div>I AM SOME TEXT</div>;6};78const ComponentWithChildren = () => {9 return (10 <>11 <TextComponent>12 <span> I am a child of Text Component!!</span>13 </TextComponent>14 </>15 );16};1718ReactDOM.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:
xxxxxxxxxx231import React from "react";2import ReactDOM from "react-dom";34const TextComponent = ({ children }) => {5 return (6 <>7 {children}8 <div>I AM SOME TEXT</div>9 </>10 );11};1213const ComponentWithChildren = () => {14 return (15 <>16 <TextComponent>17 <span> I am a child of Text Component!!</span>18 </TextComponent>19 </>20 );21};2223ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));When dealing with multiple children, you can see that they're passed as an array:
xxxxxxxxxx271import React from "react";2import ReactDOM from "react-dom";34const TextComponent = ({ children }) => {5 console.log("Children: ", children);67 return (8 <>9 {children}10 <div>I AM SOME TEXT</div>11 </>12 );13};1415const 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};2627ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));You can access specific children by their element:
xxxxxxxxxx281import React from "react";2import ReactDOM from "react-dom";34const TextComponent = ({ children }) => {5 console.log("Children: ", children);67 return (8 <>9 {children[0]}10 {children[1]}11 <div>I AM SOME TEXT</div>12 </>13 );14};1516const 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};2728ReactDOM.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):
xxxxxxxxxx291import React from "react";2import ReactDOM from "react-dom";34const TextComponent = ({ children }) => {5 console.log("Children: ", children);67 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};1617const 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};2829ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));What happens if we pass in both props AND children? The solution is rather counterintuitive:
xxxxxxxxxx261import React from "react";2import ReactDOM from "react-dom";34const TextComponent = props => {5 return (6 <>7 <span> {props.whatever} </span>8 {props.children}9 <div>I AM SOME TEXT</div>10 </>11 );12};1314const 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};2526ReactDOM.render(<ComponentWithChildren />, document.querySelector("#root"));In the TextComponent javascript, we're only passing in one single object.
xxxxxxxxxx91const 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.
xxxxxxxxxx91const 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:
xxxxxxxxxx211import React from "react";2import ReactDOM from "react-dom";34const TextComponent = ({ whatever }) => {5 return (6 <>7 <span> {whatever} </span>8 <div>I AM SOME TEXT</div>9 </>10 );11};1213const ComponentWithChildren = () => {14 return (15 <>16 <TextComponent />17 </>18 );19};2021ReactDOM.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):
xxxxxxxxxx11npm i --save prop-typesxxxxxxxxxx261import React from "react";2import ReactDOM from "react-dom";3import PropTypes from "prop-types";45const TextComponent = ({ whatever }) => {6 return (7 <>8 <span> {whatever} </span>9 <div>I AM SOME TEXT</div>10 </>11 );12};1314TextComponent.propTypes = {15 whatever: PropTypes.string.isRequired16};1718const ComponentWithChildren = () => {19 return (20 <>21 <TextComponent />22 </>23 );24};2526ReactDOM.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.