Introduction

Basic of JavaScript

  • Variables and types
  • Objects and arrays
  • Functions and classes
  • Loops and conditionals

Learning JS

Why React?

We just tell React what we want, and it will build the actual interfaces on our behalf in the web browser. Without React or similar libraries, we would need to manually build user interface with native web APIs in JavaScript, and that is not easy. When you heard the statement that React is declarative, this is exactly what it means. We describe user interfaces with React and tell it what we want, not how to do it. Reacy will take care of how and translate our declarative descriptions, which we write in the React language to actual user interfaces in the browser.

How Exactly is NOT being a framework a good thing?

Frameworks

Limited flexibility

  • Do things a certain way
  • Hard to deviate

Large and full of features

  • Hard to customize
  • Use the whole thing

A “language” to model the state of UIs, not the transactions on them.

React’s Basic Concepts

React has three simple and fundamental concepts that need to be understand. The first concept is its components. With React we describe user interfaces using components. Components are just functions. In fact, simple React components are actually just vanilla JavaScript functions.

  1. Components

    • Like functions
    • Input: props, state | Output: UI
    • Reusable and composable
    • <Component />
    • Can manage a private state
  2. Reactive updates

    • React will react
    • Take updates to the browser
  3. Virtual Views in memory

    • Generate HTML using JavaScript
    • No HTML template language
    • Tree reconciliation

React uses the virtual DOM to compare versions of the UI in memory before it acts on them.

React Components

Function Component Class Component

A react component can be one of the two types. It can be either a function component or class component. Both types can be stateful and have side effects, or they can be purely presentational. Class Component is rather powerful.

JSX is NOT HTML

1
2
3
4
5
6
7
8
9
10
11
class Hello extends React Component {
render(){
return(
<div className="container">
'<h1>Getting Started</h1>'
</div>
);
}
}

ReactDOM.render(<Hello />, mountNode);
1
2
3
4
5
6
7
8
9
10
11
class Hello extends React Component {
render(){
return(
React.createElement("div", { className: "container"},
React.createElement("h1", null, "Getting Started")
)
);
}
}

ReactDOM.render(React.createElement(Hello, null), mountNode);

Your First React Component

Playground for JavaScript

1
2
3
4
5
function Hello() {
return <div>Hello React!</div>;
}

ReactDOM.render(<Hello />, document.getElementById("mountNode"));

Hello React!

Simple React function component named Hello. It returns a div. This component has no input. It’s also a peer component, no state here. To display a React component in a browser, we need to instruct the ReactDOM library on how to do that. The function designed to do that is React.DOM.render, which takes in two arguments. The first is the component to render

The browser is not really executing this. Instead, it’s executing the following and this takes many arguments. The first argument is the element to be created, div. The second argument is any attributes this element will have, the div element has no attributes. The third argument is the child of that div element, Hello React!

React.createElement

A component name has to start with an uppercase letter, because if you don’t do that, React will think you meant a HTML element.

Always name the components with an uppercase first letter.

Your First React Hook

Playground

1
2
3
4
5
6
function Button() {
const [counter, setCounter] = useState(0);
return <button onClick={() => setCounter(counter + 1)}>{counter}</button>;
}

ReactDOM.render(<Button />, document.getElementById("mountNode"));

Continuing with what we started in the previous video, the current code is available under this URL, and our component currently renders a stateless button. We now need to make the button increment a counter every time it’s clicked. We need a state object. To use a state object, react has a special function named useState. This is also globally available here in the playground. So we’re going to be invoking this function. This useState function is going to return two items for us. The first item is a state object, and the second item is a function to change that state object. The state object can be of any time you wish it to be. It could be a string, a number, or an array, or anything else. For this use case, we need it to be a number. I’m going to name this state object counter and name its updater function setCounter.

useState() results:

  • state object (getter)
  • updater function (setter)

The syntax you need here might look a bit weird, but since JavaScript functions can only return a single value, the useState function returns an array with exactly the two elements here. To make this work, we need a variable‑defining keyword before this syntax. I’m going to use const. This special syntax here is using JavaScript destructuring feature to capture these two variables in one line. It’s not magic or anything; useState returns an array with two elements, and in here we’re destructuring these two elements into variables in our Button component. The argument to useState is the initial value for the state element, counter in our case. Since we want that counter to be a number, we’ll set that to 0. To use the two new variables that we introduced, let me tell you a nice little thing about JSX. It supports displaying dynamic expressions if you place them within curly braces anywhere inside JSX. So if I make the button’s label into curly braces, and inside these curly braces put any JavaScript expression I want, I’ll use Math.random, and execute the code, the button will have random value every time I render this component. So to use this new state element, all we have to do is put the counter variable within curly braces and make that the label of the button element. The button will now be rendered with a label of 0. This is the same 0 that’s coming from the initial value we specified for useState. So any value initialized here will show up as the button’s label, but we’ll keep it as 0. Now to use the setCounter updater function, every time we click on this button, we need to introduce an event handler. This is going to look similar to the way we can do this with the DOM API. We define an onClick attribute on the button element. This onClick attribute is also case sensitive. It has to be camelCase like this. And unlike the DOM version of the onClick, which receives a string, this react onClick attribute receives a function reference. So you always specify that inside curly braces as well. In here, we’re going to specify a function reference. Let me create a dummy function here, function. I’m going to name it logRandom, and we’ll make this function console.log(Math.random). Very simple. To use this function as the onClick event handler, we pass in here the functions reference, its name, just like this. To see the console.log messages, we need to bring up the browser’s console here. And after executing this code, every time I now click on that button, the console will print a random value. Note that when we passed in the logRandom function here, we did not invoke the function. This will not work. You just need to pass in the pointer to the function. You can also inline the function definition right here inside the curly braces. So, basically, you paste in the function definition, and this will work as well. We can make this more concise by using the new arrow function syntax, which can fit in a single line here, () =>, and then the body of the function directly after that. This highlighted part is an inline arrow function definition. We’re not invoking the function here. We’re defining it and passing this new reference to the onClick prop. This will work as well. So now that we saw how to wire an onClick event, what we need to do to make the counter count is to use the setCounter updater function that we got from useState. So instead of console logging a random value here, I’m going to use setCounter, invoke that function, and the argument to setCounter will become the new value of counter here. So if we pass in counter+1 as the argument just like this and execute this code, then every time the onClick event is triggered now, the counter will be incremented, and you’ll see the button behaving as we wanted it to. This useState function is called a hook in the react world. It is similar to a mix‑in or a module, but it is a stateful one that hooks this simple component into a state. What I need you to notice here is that to reflect the new value of the counter variable in the browser here, we did nothing. We just managed the state of the counter value. React is automatically updating the browser for us thanks to its reactive nature. We did not implement any transactions on this UI. We implemented the transactions on a JavaScript counter object in memory. Our UI implementation was basically telling React that we want the label of the button to always reflect the value of that counter object. We didn’t do any DOM updates. React did. You’re going to need a few more examples to appreciate this power. So let’s add some more features to this basic example. Let’s make our UI show many buttons and make all the buttons increment a single counter. But before we do that, let me give you a quick challenge. Instead of a simple counter, change this component to start the button with a label of 5, then double that value on each click. Go ahead and try to make this simple change, and you can see my solution under this URL.

Challenge

  • Start the button with 5
  • Double the button’s label on each click
1
2
3
4
5
6
function Button() {
const [counter, setCounter] = useState(5);
return <button onClick={() => setCounter(counter + 5)}>{counter}</button>;
}

ReactDOM.render(<Button />, document.getElementById("mountNode"));

Your First One-way Data Flow

In the previous video, we made a simple stateful Button component that renders an HTML button element and increments its numeric label when that button element is clicked. We introduced the useState React hook to manage a counter state. Let’s improve this component. First, don’t use long lines like this. They’re hard to read. So let me format this return value to make it more readable. There we go. Note how I used parentheses here, not curly braces. We’re not returning an object here. We’re returning a function call, remember, a React.createElement function call.

1
2
3
4
5
6
function Button() {
const [counter, setCounter] = useState(0);
return <button onClick={() => setCounter(counter + 1)}>{counter}</button>;
}

ReactDOM.render(<Button />, document.getElementById("mountNode"));

Second Improvement. Instead of an inline function here, let’s create an official click handler function. This new function has to be defined inside the Button component because it needs access to the counter and setCounter variables. You can use any name here, but it’s commonly named handleClick. We can just paste the code we had inline before here as the body of this function. And in the onClick attribute here, we pass a reference to handleClick. Make sure this is all good and the button is still incriminating.

1
2
3
4
5
6
7
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

ReactDOM.render(<Button />, document.getElementById("mountNode"));

So far, we’ve only worked with one component. Let’s add more. Let’s split our one Button component into two. We’ll keep the Button component to be just the incrementer, and let’s add a new Display component, which is going to just display the value of the counter. This new Display component is going to be a pure presentational one. It will not have a state of its own. That’s normal. Not every React component has to have a stateful hook. So to create a new component, we define a function named Display, and we’ll make it return some HTML element. For now, let me just put a placeholder div in here and execute.

1
2
3
4
5
6
7
8
9
10
11
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

function Display() {
return <div>...</div>;
}

ReactDOM.render(<Button />, document.getElementById("mountNode"));

Notice that this new Display component did not show up because I have not included it in the rendered element yet. I just defined it. Let’s include it. So remember to include a component, we need to use it like that. However, you can’t just add it here as an adjacent sibling to the Button element. This will not work. Question, why does that not work?

And the answer is because each one of these elements get translated into a function call. You have few options here to fix this problem.

First, you can render an array of elements here and insert into that array as many React elements as you wish. This will work. This is usually a good solution when all the elements you’re rendering are coming from the same component in a dynamic way. It’s not ideal for the case we’re doing here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

function Display() {
return <div>...</div>;
}

ReactDOM.render(
[<Button />, <Display />],
document.getElementById("mountNode")
);

The other option is to make these two React elements the children of another React element. For example, we can just enclose them in a div, create a div element, then render the Button and the Display inside this div element. The React API supports this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

function Display() {
return <div>...</div>;
}

ReactDOM.render(
<div>
<Button />
<Display />
</div>,
document.getElementById("mountNode")
);

In fact, React has a special object. If you need to enclose multiple elements like this without introducing a new div parent, you can use React.Fragment. This will do exactly the same thing that the div did, but no new DOM parent will be introduced. This case is so common in React that the JSX extension has a shortcut for it. Instead of typing React.Fragment, you can just have an empty tag. This empty tag, if supported in the environment, will get compiled to the React.Fragment version.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

function Display() {
return <div>...</div>;
}

ReactDOM.render(
<React.Fragment>
<Button />
<Display />
</React.Fragment>,
document.getElementById("mountNode")
);

For the case that we’re doing here, I think a div here is okay, so I’m going to keep that. Question, what can we do to make this better? And the answer is we should really extract this code into its own component. This new component can have any name, but you can just use App here. Go ahead and try to create this app component on your own. Make it return this DOM and use it in the ReactDOM.render call instead of what we have. We take the section, create a new function, name it App, make this function return the exact DOM tree that we have down under. And then in here, instead of all that, we can just render the App component just like that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>{counter}</button>;
}

function Display() {
return <div>...</div>;
}

function App() {
return (
<div>
<Button />
<Display />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

Since we’re going to display the counter’s value in the new Display component, we no longer need to show the counter’s value as the label of this button. Instead, I’m going to change the label to just +1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Button() {
const [counter, setCounter] = useState(0);
const handleClick = () => setCounter(counter + 1);
return <button onClick={handleClick}>+1</button>;
}

function Display() {
return <div>...</div>;
}

function App() {
return (
<div>
<Button />
<Display />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

Now we need to display the counter value as the message in the Display component. But we have a problem. We actually have two problems. The first problem is that the counter is currently a state element in the Button component, and we need to access it in the Display component, which is a sibling of the Button component in the current tree. So this is not going to work. The state in a React component can be accessed only by that component itself and no one else. To make this counter state accessible to both sibling components, we need to lift it one level up and put it in their parent component, which is the App component that we just introduced. We just move this useState line down to the App component right here. I’ll initialize the counter with a different value here to make sure things are working. The logic of this handleClick function will need to change. We will come back to that in a minute. Let’s just comment it out for now. Now that we have the counter state element in the App compartment, which is the parent of the Display component, we can flow some data from the parent to the child. In this case, we need to flow the value of the counter state into the Display component, which brings us to the mighty props object. We haven’t really used it yet, so let me tell you about it. To pass a prop to a component, you specify an attribute here, just like in HTML. You can name the props of the component anything you want. For example, I’ll make the Display component to receive a prop named message, and the value of that message is the counter variable that’s coming from the useState hook. The Display component can now use its props object, which is the argument to the function here, and it’s usually named props. You don’t really have to name it props, but that’s the convention. All function components receive this object even when they have no attributes. So the Button component is currently receiving its props object, and that object so far has been empty. Because a component can receive many attributes, this props object will have a key value pair for each attribute. This means to access the message prop and place its value within the display div, we do curly braces and use props.message.

Let me test that real quick, and we have an error, handleClick is not defined because we’ve used it here and commented it out here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Button(props) {
return <button onClick={handleClick}>+1</button>;
}

function Display(props) {
return <div>{props.message}</div>;
}

function App() {
const [counter, setCounter] = useState(42);
return (
<div>
<Button />
<Display message={counter} />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

So let me just put an empty function here to get things working, and here we go. A counter value of 42 is now getting displayed. This is coming from the Display component. And what we did here is called the one‑way flow of data. Parent components can flow their data down to children components. Parent components can also flow down behavior to their children, which is what we need to do next. In the App component, since the counter state is here now, we need a function on this level to handle updating this state. Let’s name this function incrementCounter. The logic for this function is actually the exact same logic that we had before in the handleClick function in the Button component. So we can just move it in here. This new function is going to update the App component’s counter state to increment the counter value using the previous counter value. The onClick handler in the Button component now has to change. We want it to execute the incrementCounter function that’s in the App component, but a component can only access its own functions. So to make the Button component able to invoke the incrementCounter function in the App component, we can pass a reference to incrementCounter to the Button component as a prop. Yes, props can hold functions as well, not just data. Functions are just objects in JavaScript, and you can pass any object value as a prop. We can name this new prop anything. I’ll name it onClickFunction and pass it a value of incrementCounter, which is the reference to the function we defined in the App component. We can use this new pass down behavior directly in the onClick value. It will be a prop on this component, so we can access it with props.onClickFunction. Testing, all is good.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Button(props) {
return <button onClick={props.onClickFunction}>+1</button>;
}

function Display(props) {
return <div>{props.message}</div>;
}

function App() {
const [counter, setCounter] = useState(42);
const incrementCounter = () => setCounter(counter + 1);
return (
<div>
<Button onClickFunction={incrementCounter} />
<Display message={counter} />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

Something very powerful is happening here. The onClickFunction property allowed the button to invoke the App component’s incrementCounter function. It’s like when we click that button, the Button component reaches out to the App component and says hey parent, go ahead and invoke that incrementCounter behavior now. In reality, the App component is the one in control here, and the Button component is just following generic rules. If you analyze the code as it is now, you’ll realize how the Button component has no clue what happens when it gets clicked. It just follows the rules defined by the parent and invokes a generic onClick function. The parent controls what goes into that generic behavior. That’s basically the concept of responsibility isolation. Each component here has certain responsibilities, and they get to focus on that. Look at the Display component too. From its point of view, the message value is not a state. It’s just a value that the App component is passing to it. The Display component will always display that message. This is also a separation of responsibilities. As the designer of these components, you get to choose the level of responsibilities. For example, if we want to, we can make the responsibility of displaying the counter value part of the App component itself and not use a new Display component for that, but I like it this way. This App component has the responsibility of managing the counter state. That’s an important design decision that we made, and it is one you’re going to have to make a lot in a React application, where to define the state. And the answer is usually simple, down in a tree as close as possible to the children who need to access that value on the state.

Components Reusability

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Button(props) {
// const handleClick = () => setCounter(counter+1);
return <button onClick={props.onClickFunction}>+{props.increment}</button>;
}

function Display(props) {
return <div>{props.message}</div>;
}

function App() {
const [counter, setCounter] = useState(0);
const incrementCounter = () => setCounter(counter + 1);
return (
<div>
<Button onClickFunction={incrementCounter} increment={1} />
<Button onClickFunction={incrementCounter} increment={5} />
<Button onClickFunction={incrementCounter} increment={10} />
<Button onClickFunction={incrementCounter} increment={100} />
<Display message={counter} />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

After

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
function Button(props) {
const handleClick = () => props.onClickFunction(props.increment);
return <button onClick={handleClick}>+{props.increment}</button>;
}

function Display(props) {
return <div>{props.message}</div>;
}

function App() {
const [counter, setCounter] = useState(0);
const incrementCounter = (incrementValue) =>
setCounter(counter + incrementValue);
return (
<div>
<Button onClickFunction={incrementCounter} increment={1} />
<Button onClickFunction={incrementCounter} increment={5} />
<Button onClickFunction={incrementCounter} increment={10} />
<Button onClickFunction={incrementCounter} increment={100} />
<Display message={counter} />
</div>
);
}

ReactDOM.render(<App />, document.getElementById("mountNode"));

Tree Reconciliation in Action

1
2
3
4
5
6
7
8
9
10
11
12
13
document.getElementById("mountNode").innerHTML = `
<div>
Hello HTML
</div>
`;

ReactDOM.render(
React.createElement("div", null, "Hello React"),
document.getElementById("mountNode2")
);

// currentTime: (new Date).toLocaleTimeString()
// setInterval(fn, 1000);

We’ll first have to find the element that needs changing in the DOM tree and add some more imperative logic to change its content. We are not doing that in React. We’re being declarative in React. We just told React that we’d like a pre element with the date string. No imperative logic is here, and yet we’re still getting the efficiency of a tuned‑up imperative alternative. This is the subtle power here. The React way is not only a lot more efficient, but it also removes a big layer of complexity about the way we think about updating user interfaces. Having React do all the computations about whether we should or should not update the DOM enables us to focus on thinking about our data and state and the way to model that state. We then only manage the updates that’s needed on the state without worrying about the steps needed to reflect these updates in the actual user interface in the browser because we know React will do exactly that for us, and it will do it in an efficient way.

Summary

Components and reactive updates
Virtual DOM nodes and JSX
Props and State

  • (props) => {}
  • [val, setVal] = useState(initialVal)
  • Immutable props. Mutatable state

ReactDOM.render

  • <Component />
  • DOM node

React events (onClick, onSubmit, …)

Functions and class components