Guide - React
Guide - React
INTRO
Why React?
Here are several points to consider using it to create an app o even just implement it in already existing one.
1. React is fast, apps made in React can handle complex updates and still feel quick and responsive.
2. React is modular. Instead of writing large, dense files of code, you can write many smaller, reusable files, just like with
Angular, Vue, Polymer and other frameworks or libraries.
3. React’s modularity can be a beautiful solution to JavaScript’s maintainability problems.
4. React is scalable. Large programs that display a lot of changing data are where React performs best.
5. React is flexible. You can use React for interesting projects that have nothing to do with making a web app. People are still
figuring out React’s potential.
I do believe that creating is the best way of learning, thus i created this guide with the purpose of learning, but also i could be useful
for anyone that reads it.
This guide is based on primary on CodeAcademy´s Learn React Part I & II, Maximilian Schwarzmüller´s React 16 - The Complete
guide, of course the official React documentation: https://reactjs.org/ which offers deeper understanding of may concepts we
manage on this simple guide & also used other popular sources such as stackoverflow, dev.to & medium.
Basic JSX
1 - JSX
Before learning the React core we must learn JSX, which is a syntax extension for JavaScript. It was written to be used with React.
JSX code looks a lot like HTML.
Syntax extension means that JSX is not valid JavaScript. Web browsers can’t read it, if a JavaScript file contains JSX code, then
that file will have to be compiled. That means that before the file reaches a web browser, a JSX compiler will translate any JSX into
regular JavaScript.
A basic unit of JSX is called a JSX element, here’s an example of a JSX element:
<p>Hello world</p>
JSX elements are treated as JavaScript expressions. They can go anywhere that JavaScript expressions can go, that means that a
JSX element can be saved in a variable, passed to a function, stored in an object or array, you name it.
const myArticle = <article></article>
Here is a more complex JSX element with minor features explained:
// If a JSX expression takes up more than one line, you must wrap it in parentheses.
const blog = (
{/* A JSX expression must have exactly one outermost element. */}
{/* You can nest JSX elements inside of other JSX elements, just like in HTML. */}
<div>
{/* JSX elements can have attributes, just like HTML elements can. */}
<img src="pics/192940u73.jpg" />
<h1>
Welcome to Dan's Blog!
</h1>
<article>
Wow I had the tastiest sandwich today. I <strong>literally</strong> almost freaked out.
</article>
</div>
);
ReactDOM.render() is the most common way to render JSX. It takes a JSX expression, creates a corresponding tree of DOM nodes,
and adds that tree to the DOM. That is the way to make a JSX expression appear on screen.
One special thing about ReactDOM.render() is that it only updates DOM elements that have changed.
That means that if you render the exact same thing twice in a row, the second render will do nothing and that is pretty good for the
performance of your app.
app.js\
import React from 'react';
/* ReactDOM is the name of a JavaScript library. This library contains several React-specific methods, all of which deal with the
DOM in some way or another. */
import ReactDOM from 'react-dom';
const myList = (
<ul>
<li>Learn React</li>
<li>Become a Developer</li>
</ul>
);
ReactDOM.render(
// ReactDOM.render()‘s first argument should be a JSX expression
myList,
// The first argument is appended to whatever element is selected by the second argument.
document.getElementById('app')
);
Here in our index ReactDOM will select our app div element and append our JSX element:
index.js\
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Learn ReactJS</title>
</head>
<body>
<div id="app"></div>
<script src="/app.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"
></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
></script>
</body>
</html>
ADVANCED JSX
2 - ADVANCED JSX
Grammar in JSX is mostly the same as in HTML, but there are subtle differences to watch out for. Probably the most frequent of
these involves the word class.
Most HTML elements use two tags: an opening tag (<div>), and a closing tag (</div>). However, some HTML elements such as
<img> and <input> use only one tag. The tag that belongs to a single-tag element isn’t an opening tag nor a closing tag; it’s a
self-closing tag.
But, in JSX, you have to include the slash. If you write a self-closing tag in JSX and forget the slash, you will raise an error.
// Fine in JSX:
<br />
Everything inside of the curly braces inside JSX will be treated as regular JavaScript.
ReactDOM.render(
<h1>2 + 3 = {2 + 3}</h1>
document.getElementById('app')
);
// Expected Render inside the h1 tag: 5
When you inject JavaScript into JSX, that JavaScript is part of the same environment as the rest of the JavaScript in your file.
That means that you can access variables while inside of a JSX expression, even if those variables were declared outside.
// Declare a variable:
const name = 'Gerdo';
2.5 - When should I assign a variable to a JavaScript expression that I want to use in a JSX expression?
The use of variables to store JavaScript expressions will largely be based on preference. However, we will usually want to use
variables assigned to our JS expressions when our JS code would otherwise be hard to read/follow before using our JS expression
inside of our JSX.
const myFunc = (a, b) => {
// Do some logic or calculations with the parameters here
}
/* Here, we assign a function to the variable `myFunc` then call the myFunc function from inside our JSX - this is especially
useful if the logic inside myFunc would be difficult to read and understand from inside a JSX expression */
ReactDOM.render(<h1>{myFunc(3,4)}</h1>, document.getElementById('app'));
const pandaImage = (
<img
src="images/panda.jpg"
alt="panda"
height={sideLength}
width={sideLength}
/>
);
const panda = (
<img
src={pics.panda}
alt="Lazy Panda" />
);
JSX elements can have event listeners, just like HTML elements can. Programming in React means constantly working with event
listeners.
You create an event listener by giving a JSX element a special attribute. Here’s an example:
// First we declare our JS function
function myFunc() {
alert("Hello, this is my function.");
}
// Then we can simply call our method with a world formed by on + a valid event
// JSX Event listeners should be written in camelCase
<img onClick={myFunc} />
An event listener attribute’s name should be something like onClick or onMouseOver: the word on, plus the type of event that you’re
listening for.
Here’s a rule that you need to know: you can not inject an if statement into a JSX expression.
const pics = {
kitty:
"https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-kitty.jpg",
doggy:
"https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-puppy.jpeg"
};
let img;
A more compact way to write conditionals in JSX is the ternary operator, it works the same way in React as it does in regular
JavaScript.
you write x ? y : z, where x, y, and z are all JavaScript expressions. When your code is executed, x is evaluated as either “truthy” or
“falsy.” If x is truthy, then the entire ternary operator returns y. If x is falsy, then the entire ternary operator returns z.
function coinToss() {
// Randomly return either 'heads' or 'tails'.
return Math.random() < 0.5 ? "heads" : "tails";
};
const pics = {
kitty:
"https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-kitty.jpg",
doggy:
"https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-puppy.jpeg"
};
Like the ternary operator, && is not React-specific, but it shows up in React surprisingly often.
&& works best in conditionals that will sometimes do an action, but other times do nothing at all.
// judgmental will be true half the time.
const judgmental = Math.random() < 0.5;
const favoriteFoods = (
<div>
<h1>My Favorite Foods</h1>
<ul>
<li>Sushi Burrito</li>
<li>Rhubarb Pie</li>
{!judgmental && <li>Nacho Cheez Straight Out The Jar</li>}
<li>Broiled Grapefruit</li>
</ul>
</div>
);
/* <li>Nacho Cheez Straight Out The Jar</li> will only appear if !judgmental which
has a 50% of being truthy */
The array method .map() comes up often in React. It’s good to get in the habit of using it alongside JSX.
If you want to create a list of JSX elements, then .map() is often your best bet. It can look odd at first:
const people = ["Rowe", "Prevost", "Gare"];
ReactDOM.render(peopleRend, document.getElementById("app"));
2.12 - Keys
When you make a list in JSX, sometimes your list will need to include something called keys:
<ul>
<li key="li-01">Rowe</li>
<li key="li-02">Prevost</li>
<li key="li-03">Gare</li>
</ul>
A key is a JSX attribute. The attribute’s name is key. The attribute’s value should be something unique, similar to an id attribute.
keys don’t do anything that we can see, React uses them internally to keep track of lists. If you don’t use keys when you’re
supposed to, React might accidentally scramble your list-items into the wrong order.
Not all lists need to have keys. A list needs keys if either of the following are true:
1. The list-items have memory from one render to the next. For instance, when a to-do list renders, each item must “remember
whether it was checked off. The items shouldn’t get amnesia when they render.
2. A list’s order might be shuffled. For instance, a list of search results might be shuffled from one render to the next.
If neither of these conditions are true, then we don’t have to worry about keys.
// Add an i parameter to .map()‘s inner function, so that we can access each person’s unique index
const peopleLis = people.map((person, index) => (
/* We can get a unique key on each loop, by adding the {'person_' + i} attribute to our <li></li> */
<li key={"person_" + index}>{person}</li>
));
You can write React code without using JSX, here is a common JSX element:
const h1 = <h1>Hello world</h1>;
we can write the same element without JSX the following way:
const h1 = React.createElement(
"h1",
null,
"Hello, world"
);
We would use React.createElement() instead of JSX when we do not want to set up compilation for our project, which the use of
JSX requires.
What is a component?
A component is a small, reusable chunk of code that is responsible for one job. That job is often to render some HTML.
When creating components, you have the choice between two different
ways:
1. Functional components (also referred to as "presentational", "dumb" or
"stateless" components - more about this later in the course)
const cmp = () => {
return <div>some JSX</div>
}
//(using ES6 arrow functions as shown here is recommended but optional)
Take a look at the code below. This code will create and render a new React class based component:
// create a variable named React
// evaluate this variable and get a particular, imported JavaScript object:
React // { imported object properties here... }
/* This imported object contains methods that you need in order to use React.
The object is called the React library. */
import React from 'react';
/* The methods imported from 'react-dom' are meant for interacting with
the DOM. */
import ReactDOM from 'react-dom';
But what is a component class needs to be called so? A component class needs a set of instructions, which tell the component
class how to build components. When you make a new component class, these instructions are the body of your class declaration.
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
You can, and often will, inject JavaScript into JSX inside of a render function.
import React from 'react';
import ReactDOM from 'react-dom';
const owl = {
title: 'Excellent Owl',
src: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-owl.jpg'
};
ReactDOM.render(
<Owl />,
document.getElementById('app')
);
Notice that the if statement is located inside of the render function, but before the return statement. This is pretty much the only
way that you will ever see an if statement used in a render function.
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<TonightsPlan />,
document.getElementById('app')
);
You are especially likely to see this inside of the body of a component class declaration. Here’s an example:
class IceCreamGuy extends React.Component {
get food() {
return 'ice cream';
}
render() {
return <h1>I like {this.food}.</h1>;
}
}
this refers to an instance of IceCreamGuy. The less simple answer is that this refers to the object on which this‘s enclosing method,
in this case .render(), is called. It is almost inevitable that this object will be an instance of IceCreamGuy, but technically it could be
something else.
Let’s assume that this refers to an instance of your component class, as will be the case in all examples in this course.
IceCreamGuy has two methods: .food and .render(). Since this will evaluate to an instance of IceCreamGuy, this.food will evaluate
to a call of IceCreamGuy‘s .food method. This method will, in turn, evaluate to the string “ice cream.”
You don’t need those parentheses because .food is a getter method. You can tell this from the get in the above class declaration
body.
There’s nothing React-specific about getter methods, nor about this behaving in this way! However, in React you will see this used in
this way almost constantly.
this in JavaScript can be a difficult concept! Here is a good resource for understanding this in Javascript:
https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/
render() {
return <button onClick={this.scream} >AAAAAH!</button>;
}
}
4.6 This.Props
If you want to pass information that isn’t a string, then wrap that information in curly braces. Here’s how you would pass an array:
<Greeting myInfo={["top", "secret", "lol"]} />
In this next example, we pass several pieces of information to <Greeting />. The values that aren’t strings are wrapped in curly
braces:
<Greeting name="Frarthur" town="Flundon" age={2} haunted={false} />
props is the name of the object that stores passed-in information. this.props refers to that storage object. At the same time, each
piece of passed-in information is called a prop. This means that props could refer to two pieces of passed-in information, or it
could refer to the object that stores those pieces of information :(
You can do more with props than just display them. You can also use props to make decisions.
Greeting.js \ (children of App.js)
import React from 'react';
import ReactDOM from 'react-dom';
We just assign the value signedIn to true and name to a string with a name.
App.js \ (parent of Greeting.js)
<Greeting name="Carlos" signedIn={true} />
You define an event handler as a method on the component class, just like the render method. Almost all functions that you define
in React will be defined in this way, as methods in a class.
class Example extends React.Component {
/* We define an event handler method with similar syntax as the render
method. */
handleEvent() {
alert(`I am an event handler.
If you see this message,
then I have been called.`);
}
render() {
return (
<h1 onClick={this.handleEvent}>
Hello world
</h1>
);
}
}
The same way that you attach any event handler to a JSX element: you give that JSX element a special attribute. The attribute’s
name should be something like onClick or onHover. The attribute’s value should be the event handler that you want to attach.
render() {
return <Button talk={this.talk} />;
}
}
4.6.5 - Naming conventions of the event handlers and props that pass event handlers
Here’s how the naming convention works: first, think about what type of event you are listening for. In our example, the event type
was “click.”
If you are listening for a “click” event, then you name your event handler handleClick. If you are listening for a “keyPress” event, then
you name your event handler handleKeyPress.
render() {
return <Button talk={this.handleClick} />;
}
}
Your prop name should be the word on, plus your event type. If you are listening for a “click” event, then you name your prop
onClick. If you are listening for a “keyPress” event, then you name your prop onKeyPress:
render() {
return <Button onClick={this.handleClick} />;
}
}
4.6.6 - this.props.children
Every component’s props object has a property named children. this.props.children will return everything in between a component’s
opening and closing JSX tags.
So far, all of the components that you’ve seen have been self-closing tags, such as <MyComponentClass />. They don’t have to be!
You could write <MyComponentClass></MyComponentClass>, and it would still work.
If a component has more than one child between its JSX tags, then this.props.children will return those children in an array.
However, if a component has only one child, then this.props.children will return the single child, not wrapped in an array.
We can set a set default props for our components to receive in case nobody passes a value to our component and this value will
be overwritten if we pass a value to our component.
class Button extends React.Component {
render() {
return (
<button>
{this.props.text}
</button>
);
}
}
ReactDOM.render(
<Button />,
document.getElementById('app')
);
4.7 - State
4.7.1 - Introduction
React components will often need dynamic information in order to render. For example, imagine a component that displays the
score of a basketball game. The score of the game might change over time, meaning that the score is dynamic. Our component will
have to know the score, a piece of dynamic information, in order to render in a useful way.
There are two ways for a component to get dynamic information: props and state. Besides props and state, every value used in a
component should always stay exactly the same.
Unlike props, a component’s state is not passed in from the outside. A component decides its own state.
render() {
return <div></div>;
}
}
this.setState() takes two arguments: an object that will update the component’s state, and a callback. You basically never need the
callback.
In the following example we explain in 3 simple steps what happens in our component to update our state and also why .bind(this)
is necessary.
import React from 'react';
import ReactDOM from 'react-dom';
toggleMood() {
const newMood = this.state.mood === 'good' ? 'bad' : 'good';
// 3. this.setState() is called and the component’s state is changed
this.setState({ mood: newMood });
}
render() {
return (
<div>
<h1>I'm feeling {this.state.mood}!</h1>
{/* 1. A user triggers an event which is being listened by onClick
2. When this listened-for event occurs, it calls an event handler
function in this case toggleMood */}
<button onClick={this.toggleMood}>
Click Me
</button>
</div>
);
}
}
Any time that you call this.setState(), this.setState() AUTOMATICALLY calls .render() as soon as the state has changed.
Think of this.setState() as actually being two things: this.setState(), immediately followed by .render().
That is why you can’t call this.setState() from inside of the .render() method! this.setState() automatically calls .render(). If .render()
calls this.setState(), then an infinite loop is created.
You learned earlier that a component can change its state by calling this.setState(). You may have been wondering: how does a
component change its props?
A component should never update this.props. Look at Bad.js to see an example of what not to do.
Our programming pattern uses two React components: a stateful component, and a stateless component. “Stateful” describes any
component that has a state property; “stateless” describes any component that does not.
render (){
return (
<Child name={this.state.name}></Child>
);
}
}
Child.js \ (child of Parent.js)
import React from 'react';
In the following example, our child component will update the state of the parent component.
changeName(newName) {
this.setState({
name: newName
});
}
render() {
return <Child name={this.state.name} onChange={this.changeName} />
}
}
handleChange(e) {
const name = e.target.value;
this.props.onChange(name);
}
render() {
return (
<div>
<h1>
Hey my name is {this.props.name}!
</h1>
<select id="great-names" onChange={this.handleChange}>
<option value="Frarthur">
Frarthur
</option>
<option value="Gromulus">
Gromulus
</option>
<option value="Thinkpiece">
Thinkpiece
</option>
</select>
</div>
);
}
}
One of the very first things that you learned about components is that they should only have one job.
You should make like Solomon and divide Child in two: one component for displaying the name, and a different component for
allowing a user to change the name.
That brings us to the essential new concept for this lesson: you will have one stateless component display information which is
called ”Sibling”, and a different stateless component offer the ability to change that information which in this case we will be calling
“Child”.
In the parent component we will handle the changes submitted by Child with a function that will update the state and “Sibling” will
recibe that state change and update its prop.
class Parent extends React.Component {
constructor(props) {
super(props);
this.changeName = this.changeName.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
render() {
return (
<div>
<Child onChange={this.changeName} />
<Sibling name={this.state.name} />
</div>
);
}
}
);
Child now will only update its value and will emit “onChange” with a payload with the value of this update.
export class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const name = e.target.value;
this.props.onChange(name);
}
render() {
return (
<div>
<select
id="great-names"
onChange={this.handleChange}>
<option value="Frarthur">Frarthur</option>
<option value="Gromulus">Gromulus</option>
<option value="Thinkpiece">Thinkpiece</option>
</select>
</div>
);
}
}
return (
<div>
<h1>Hey, my name is {name}</h1>
</div>
);
}
}
You’ll learn how to make a stateless functional component, how to make a propType, how to write a form, and how to use styles.
You’ll also be introduced to your second programming pattern: dividing components into presentational components and container
components.
5 - Styles
Defining a variable named style in the top-level scope would be an extremely bad idea in many JavaScript environments! In React,
however, it’s totally fine.
const styles = {
color: 'darkcyan',
background: 'mintcream'
};
export class StyledClass extends React.Component {
render() {
return (
<h1 style={styles}>
Hello world
</h1>
);
}
}
Also in React, if you write a style value as a number, then the unit "px" is assumed.
How convenient! If you want a font size of 30px, you can write:
const styles = {
fontSize: 30
};
Then we can just import then and use them in our elements.
Home.js \
import {styles} from './styles';
const divStyle = {
background: styles.background
};
Separating container components from presentational components is a popular React programming pattern.
Here’s the basic idea behind it: if a component has to have state, make calculations based on props, or manage any other complex
logic, then that component shouldn’t also have to render HTML-like JSX.
Instead of rendering HTML-like JSX, the component should render another component. It should be that component’s job to render
HTML-like JSX.
Remember that a presentational component will always get rendered by a container component. so the container component will
be the one that call ReactDOM to render not the presentational.
Presentational components should only have the render function, many times also a value that comes from props.
When you separate a container component from a presentational component, the presentational component will always end up like
this: one render() function, and no other properties.
If you have a component class with nothing but a render function, then you can rewrite that component class in a very different
way. Instead of using React.Component, you can write it as a JavaScript function!
A component class written as a function is called a stateless functional component. Stateless functional components have some
advantages over typical component classes. We’ll cover those advantages in this lesson.
5.7 - PropTypes
5.7.1 - PropTypes in Class Based Components
propTypes are useful for two reasons. The first reason is prop validation.
Validation can ensure that your props are doing what they’re supposed to be doing. If props are missing, or if they’re present but
they aren’t what you’re expecting, then a warning will print in the console.
Documenting props makes it easier to glance at a file and quickly understand the component class inside. When you have a lot of
files, and you will, this can be a huge benefit.
If a component class expects a prop, then you can give that component class a propType.
import React from 'react';
import PropTypes from "prop-types";
Runner has six propTypes! Look at each one. Note that bool and func are abbreviated, but all other datatypes are spelled normally.
If you add .isRequired to a propType, then you will get a console warning if that prop isn’t sent.
Runner.propTypes = {
message: React.PropTypes.string.isRequired,
style: React.PropTypes.object.isRequired,
isMetric: React.PropTypes.bool.isRequired,
miles: React.PropTypes.number.isRequired,
milesToKM: React.PropTypes.func.isRequired,
races: React.PropTypes.array.isRequired
};
It turns out the process is fairly similar. To write propTypes for a stateless functional component, you define a propTypes object as
a property of the stateless functional component itself. Here’s what that looks like:
import PropTypes from "prop-types";
const Example = (props) => {
return <h1>{props.message}</h1>;
}
Example.propTypes = {
message: PropTypes.string.isRequired
};
Think about how forms work in a typical, non-React environment. A user types some data into a form’s input fields, and the server
doesn’t know about it. The server remains clueless until the user hits a “submit” button, which sends all of the form’s data over to
the server simultaneously.
In React, as in many other JavaScript environments, this is not the best way of doing things.
The problem is the period of time during which a form thinks that a user has typed one thing, but the server thinks that the user has
typed a different thing. What if, during that time, a third part of the website needs to know what a user has typed? It could ask the
form or the server and get two different answers. In a complex JavaScript app with many moving, interdependent parts, this kind of
conflict can easily lead to problems.
In a React form, you want the server to know about every new character or deletion, as soon as it happens. That way, your screen
will always be in sync with the rest of your application.
When a user types or deletes in the <input />, then that will trigger a change event, which will call handleUserInput, now let's
imagine some of the possibilities of what handleUserInput could do, it could easily make a petition to a server of our own to
validate its value or we can validate this value with our own methods or components for each user keystroke.
export class Input extends React.Component {
constructor(props){
super(props);
this.state = {
userInput:''
};
/* As always we need to bind 'this' inside our constructor to any method
that will use 'this' */
this.handleUserInput = this.handleUserInput.bind(this);
}
render() {
return (
<div>
{/* With every keystroke we call handleUserInput which updates
userInput */}
<input type="text"
value={this.state.userInput}
onChange={this.handleUserInput}
/>
{/* Whenever the user chages the value of our input it will be
seen here */}
<h1>{this.state.userInput}</h1>
</div>
);
}
}
5.8.2 - Controlled vs Uncontrolled
There are two terms that will probably come up when you talk about React forms: controlled component and uncontrolled
component. Like automatic binding, controlled vs uncontrolled components is a topic that you should be familiar with, but don’t
need to understand deeply at this point.
An uncontrolled component is a component that maintains its own internal state. A controlled component is a component that
does not maintain any internal state. Since a controlled component has no state, it must be controlled by someone else.
Think of a typical <input type='text' /> element. It appears onscreen as a text box. If you need to know what text is currently in the
box, then you can ask the <input />, possibly with some code like this:
let input = document.querySelector('input[type="text"]');
let typedText = input.value; // input.value will be equal to whatever text is currently in the text box.
The important thing here is that the <input /> keeps track of its own text. You can ask it what its text is at any time, and it will be
able to tell you.
The fact that <input /> keeps track of information makes it an uncontrolled component. It maintains its own internal state, by
remembering data about itself.
A controlled component, on the other hand, has no memory. If you ask it for information about itself, then it will have to get that
information through props. Most React components are controlled.
In React, when you give an <input /> a value attribute, then something strange happens: the <input /> BECOMES controlled. It stops
using its internal storage. This is a more ‘React’ way of doing things.
You can find more information about controlled and uncontrolled components here:
https://reactjs.org/docs/forms.html
Lifecycle methods are methods that get called at certain moments in a component’s life.
You can write a lifecycle method that gets called right before a component renders for the first time.
You can write a lifecycle method that gets called right after a component renders, every time except for the first time.
You can attach lifecycle methods to a lot of different moments in a component’s life.
There are three categories of lifecycle methods: mounting, updating, and unmounting. This lesson is about the first category:
mounting lifecycle methods.
A component “mounts” when it renders for the first time. This is when mounting lifecycle methods get called.
componentWillMount
render
componentDidMount
5.9.2 - componentWillMount
When a component renders for the first time, componentWillMount gets called right before render.
export class Example extends React.Component {
componentWillMount() {
alert('component is about to mount! (this will be only appear once unless the component
is unmounted)');
}
render() {
return <h1>Hello world</h1>;
}
}
5.9.3 - render
We won’t go over render here - we’ve already talked about it plenty. However, you should understand how render fits into the
mounting period. Whenever a component mounts, componentWillMount is called first, followed by render, followed by
componentDidMount.
render belongs to two categories: mounting lifecycle methods, and updating lifecycle methods.
5.9.4 - componentDidMount
When a component renders for the first time, componentDidMount gets called right after the HTML from render has finished
loading.
If your React app uses AJAX to fetch initial data from an API, then componentDidMount is the place to make that AJAX call. More
generally, componentDidMount is a good place to connect a React app to external applications, such as web APIs or JavaScript
frameworks. componentDidMount is also the place to set timers using setTimeout or setInterval.
export class Example extends React.Component {
componentDidMount() {
alert('component just finished mounting!');
}
render() {
return <h1>Hello world</h1>;
}
}
5.10.1 - Intro
There are two categories that we haven’t yet discussed: updating and unmounting lifecycle methods. This lesson covers both.
What is updating?
The first time that a component instance renders, it does not update. A component updates every time that it renders, starting with
the second render.
1.componentWillReceiveProps
2.shouldComponentUpdate
3.componentWillUpdate
4.render
5.componentDidUpdate
Whenever a component instance updates, it automatically calls all five of these methods, in order.
5.10.2 - componentWillReceiveProps
As one might expect, componentWillReceiveProps only gets called if the component will receive props.
// componentWillReceiveProps will get called here:
ReactDOM.render(
<Example prop="myVal" />,
document.getElementById('app')
);
componentWillReceiveProps automatically gets passed one argument: an object called nextProps. nextProps is a preview of the
upcoming props object that the component is about to receive.
componentWillReceiveProps (nextProps){
/* Here we can compare a value our component will receive vs
a value of its own state */
if (nextProps.<prop name> > this.state.<property name>) {
// Do something here...
}
}
5.10.3 - shouldComponentUpdate
When a component updates, shouldComponentUpdate gets called after componentWillReceiveProps, but still before the rendering
begins.
If shouldComponentUpdate returns true, then nothing noticeable happens. But if shouldComponentUpdate returns false, then the
component will not update! None of the remaining lifecycle methods for that updating period will be called, including render. the
following will not be called:
1.componentWillUpdate
2.render
3.componentDidUpdate
The best way to use shouldComponentUpdate is to have it return false only under certain conditions. If those conditions are met,
then your component will not update.
shouldComponentUpdate automatically receives two arguments: nextProps and nextState. It’s typical to compare nextProps and
nextState to the current this.props and this.state, and use the results to decide what to do.
You can use shouldComponentUpdate to make Target only rerender when it actually needs to.
shouldComponentUpdate(nextProps, nextState) {
if ((this.props.text == nextProps.text) &&
(this.state.subtext == nextState.subtext)) {
alert("Props and state haven't changed, so I'm not gonna update!");
return false;
} else {
alert("Okay fine I will update.")
return true;
}
}
5.10.4 - componentWillUpdate
The third updating lifecycle method is componentWillUpdate.
You cannot call this.setState from the body of componentWillUpdate! Which begs the question, why would you use it?
The main purpose of componentWillUpdate is to interact with things outside of the React architecture. If you need to do non-React
setup before a component renders, such as checking the window size or interacting with an API, then componentWillUpdate is a
good place to do that.
If that sounds abstract, that’s okay! All of the lifecycle methods might feel a bit theoretical, until you’ve used them in real-life
scenarios.
5.10.5 - componentDidUpdate
When a component instance updates, componentDidUpdate gets called after any rendered HTML has finished loading.
componentDidUpdate automatically gets passed two arguments: prevProps and prevState. prevProps and prevState are references
to the component’s props and state before the current updating period began. You can compare them to the current props and
state.
componentDidUpdate is usually used for interacting with things outside of the React environment, like the browser or APIs. It’s
similar to componentWillUpdate in that way, except that it gets called after render instead of before.
componentDidUpdate(prevProps, prevState) {
alert('Component is done rendering!');
}
5.10.6 - componentWillUnmount
A component’s unmounting period occurs when the component is removed from the DOM. This could happen if the DOM is
re-rendered without the component, or if the user navigates to a different website or closes their web browser.
componentWillUnmount gets called right before a component is removed from the DOM. If a component initiates any methods that
require cleanup, then componentWillUnmount is where you should put that cleanup.
The following component create an interval an when it gets unmounted it clears it.
Enthused.js \ (child of App.js)
export class Enthused extends React.Component {
/* This method that calls a setInterval function, which adds an
exclamation point to some text every 15ms. */
componentDidMount() {
this.interval = setInterval(() => {
this.props.addText('!');
}, 15);
}
Here the parent component conditions the mounting of components between re-renders.
App.js \ (parent of Enthused.js)
render() {
let button;
/* Between re-renders made by state change we will condition the mounting
and unmounting of Ethused component and the button element, when one is
mounted the other will be unmounted */
if (this.state.enthused) {
button = (
<Enthused toggle={this.toggleEnthusiasm} addText={this.addText} />
);
} else {
button = (
<button onClick={this.toggleEnthusiasm}>
Add Enthusiasm!
</button>
);
}
return (
<div>
<h1>Auto-Enthusiasm</h1>
<textarea rows="7" cols="40" value={this.state.text}
onChange={this.handleChange}>
</textarea>
{button}
<h2>{this.state.text}</h2>
</div>
);
}
5.11 - Refs
Refs are created using React.createRef() and attached to React elements via the ref attribute. Refs are commonly assigned to an
instance property when a component is constructed so they can be referenced throughout the component.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
When a ref is passed to an element in render, a reference to the node becomes accessible at the current attribute of the ref.
const node = this.myRef.current;
REDUX Intro
The central store stores the entire application state, it's that simple, you can think about it as a giant javascript object.
Before anything can be be modified in our central store we need to set it:
const initialState = {
counter: 0
};
1.Our component wants to manipulate the app state so it dispatches an action, which is dispatched from your javascript code. And
action is just information package in the end with a type, something like:
store.dispatch({ type: 'ADD_COUNTER', value: 10 });
*possibly it also holds a payload.
that action doesn't hold any logic, it doesn't know how to update the store, it's just a messenger. The thing changing the store is a
reducer.
2.That action reaches the reducer, which is the thing that actually changes the store.
the reducer can check the type of the action, for example if it's addIngredient and we then define the code for that type of action in
the reducer. The reducer in the end is just a pure function which receives the action and the old state as input and which then spits
out an updated state.
const rootReducer = (state = initialState,action) => {
if (action.type === 'ADD_COUNTER'){
return {
...state,
counter: state.counter + action.value
};
}
return state;
};
The important thing is that the reducer has to execute synchronous code only, no asynchronous code.
3.The store triggers all subscriptions whenever the state changes, whenever the state is updated in the store.
And of course our component can subscribe to store updates and it then receives that update automatically, this is how simple it is.
store.subscribe(() => {
console.log('[Subscription]', store.getState());
// Expected output: [Subscription] { counter: 10 }
});
Install Redux
$ npm install --save redux
Install Redux for React
$ npm install --save react-redux
The following file shows us the basic boilerplate to connect Redux to our React App.
Index.js \
import React from 'react';
import ReactDOM from 'react-dom';
/* We need to import redux on the first place, it will allow us to create
our store */
import { createStore } from 'redux';
// This import will allow us to connect React with Redux
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
// We need to import our reducer file
import reducer from './store/reducer';
/* We create our store const using the function from our redux import and
as parameter we use the reducer file we created */
const store = createStore(reducer);
ReactDOM.render(
/* To use Redux we need to wrap our app between our Provider and add
the property store with the value of our const store */
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
registerServiceWorker();
Our basic reducer can be just our initial state and a simple reducer.
reducer.js \
const initialState = {
counter: 0
}
We can make our store available in our component with five simple and very boilerplate steps.
2. Import connect.
Counter.js \
// connect will allow us to connect our store with our component
import { connect } from 'react-redux';
3. create a const that is a function that returns an object with the value(s) that come(s) from the store.
/* this function allow us to receive props from our store, the state parameter here is the state
of our store */
const mapStateToProps = state => {
return {
ctr: state.counter
};
};
4. export connect with our const as parameter and our component as the returned value
/* connect is a high order component, it's a function which returns a higher order component,
that is why Counter is in parenthesis at the end */
export default connect(mapStateToProps)(Counter);
5. Get the value from the store as a prop
<CounterOutput value={this.props.ctr} />
To add a way to dispatch actions to our store from our component we simply follow step 1 from above, then:
1. We are just dispatching a javascript object and only the type property is set in stone, so to say. We can obviously add more
properties to that object, no one is stopping us from doing that, we can simply add a second property, and it is a good practice to
call that one payload, because that is what it is and inside of it we can send data.
onAddCounter: () => dispatch({ type: 'ADD', payload: { value: 5 } }),
2. Then inside our reducer we can easily get our date from our action object.
const reducer = (state = initialState, action) => {
...
if (action.type === 'ADD' ){
return {
...state,
counter: state.counter + action.payload.value
}
}
reducer.js \
case 'STORE_RESULT':
return {
...state,
// concat returns a new array so we do not modify our original one
results: state.results.concat({
id: new Date(),
value: state.counter
})
}
Counter.js \
<li key={storeResult.id} onClick={ () => this.props.onDeleteResult(storeResult.id) }>
{storeResult.value}</li>
reducer.js \
case 'DELETE_RESULT':
// We can use filter to clone our array without mutating our original one
const updatedArray = state.results.filter(
(result) => result.id !== action.payload.resultElementId);
return {
...state,
results: updatedArray
}
The key to updating nested data is that every level of nesting must be copied and updated appropriately. This is often a difficult
concept for those learning Redux, and there are some specific problems that frequently occur when trying to update nested
objects. These lead to accidental direct mutation, and should be avoided.
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
There always is the danger of us simply adding a tiny typo in our dispatch actions and searching for it for hours because we don't
find it. Therefore it is a good practice to outsource your action types into constants you can use in your application so that you
always just import a constant and eliminate the danger of mistyping, this is especially useful as your application grows.
actions.js \
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const ADD = 'ADD';
export const SUBTRACT = 'SUBTRACT';
export const STORE_RESULT = 'STORE_RESULT';
export const DELETE_RESULT = 'DELETE_RESULT';
reducer.js \
import * as actionTypes from './actions';
case 'STORE_RESULT':
case actionTypes.STORE_RESULT:
We can do the same in our components, in this case our container component
Counter.js \
onIncrementCounter: () => dispatch({ type: actionTypes.INCREMENT }),
All actions in the end get funneled through one reducer but redux, the package gives us a utility method we can use to combine
multiple reducers into one, so that we still follow the pattern of having only one reducer behind the scenes but for us as a developer,
that we can split up our code logically so that we don't get one huge reducer.
const initialState = {
results: []
}
4. Inside our components we need to change our mapStateToProps, we can no longer get our centralized state directly from
state.
Counter.js \
/* this function allow us to receive props from our store, the state parameter here is the state
of our store */
const mapStateToProps = state => {
return {
counter: state.counterState.counter,
results: state.resultsState.results
};
};
To manage other state values inside a reducer that it does not owns, that value that value must always come from a payload.
case actionTypes.STORE_RESULT:
return {
...state,
// concat returns a new array so we do not modify our original one
results: state.results.concat({
id: new Date(),
value: action.payload.result
})
}
Well the question whether you use redux or not depends on the size of your application and the complexity
of your state.
Let's consider local UI state such as showing or hiding a backdrop, opening a modal, all these things which of course change the
state to update the UI of your react application and hence to show something different, should you use redux for that? The answer
is often times, you might not use redux here, you mostly handle this within your components, that being said, you can use redux for
that.
Another important type of state is persistent state, this means the state you typically also store in server side databases like the
users of your application or posts of a blog, all burger orders, stuff like that. Now here, you typically do use redux but of course not
for all the data you have in your service side database because redux of course is just for managing the state in your application as
long as your application is alive. And always keep in mind, when the user refreshes your page, your state is gone so redux is not a
replacement for a database, instead you store such data on a server but the relevant slices are managed by redux. So the post
you're currently displaying, the users you currently need to display, the post the user currently may edit, these things are loaded and
stored in redux so that you have them available so that you can render them to the screen.
you definitely need to be aware of the current filter settings on your client in your javascript code in the react application
though.This is state you definitely use redux for, you managed that via redux because it might affect multiple components or areas
of your application, for example if the user is authenticated, it might be important for a lot of components in your app and there,
redux really shines because the central storage then offers a huge advantage.
7 - MiddleWare
Middleware basically is a term used for functions or the code general you hook into a process which then gets executed as part of
that process without stopping it.
In the redux context we can add middleware and our actions will still reach our reducers thereafter but we can do something with
those actions before it reaches our reducer, that can be simply logging something, but that will also become important later when
we want to execute asynchronous code.
We can simply add a Middleware to our central state the following way
Index.js \
We need to import applyMiddleware
import { createStore, combineReducers, applyMiddleware } from 'redux';
We create our middleware
/* This middleware logs each action we do to our central state, it will get the
store as a parameter because we use a method provided by redux, that will give us
the store */
const logger = store => {
/* We call this parameter 'next' because this will be a function which you can
execute to let the action continue its journey onto the reducer */
return next => {
// Next returns an action we dispatch as an input
return action => {
console.log('[Middleware] Dispatching:', action);
const result = next(action);
console.log('[Middleware] next state:', store.getState());
return result;
}
}
};
We add appyMiddleware to our createStore
const store = createStore(rootReducer, applyMiddleware(logger) );
The Redux DevTools are specialized tools, that allows us to debug our apps that are using redux and others things like watching our
current state and its previous values.
To use them we need to install them as an addon in our browser and them follow the next steps:
Index.js \
We need to import compose to use compose to compose a set of enhancers with both the dev tools features and our middleware.
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
We create a const to store our compose.
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
Finally we wrap our applyMiddleware created in the last chapter with our created function composeEnhancers.
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(logger)) );
Now I want to introduce a new way of creating actions, so-called action creators, how does that look like? What is that? An action
creator is just a function which returns an action or which creates an action, hence the name.
Since we are going to have more actions in the future this folder structure works better.
We can create a simple action creator this way, it is just a function that returns an object.
actions.js \
export const increment = () => {
return {
type: INCREMENT
};
};
But a better way of import then would be the ‘import everything’ way and give it a name.
import * as actionCreators from '../../store/actions/actions';
In this case we need to call our function through the name we gave to our import.
onIncrementCounter: () => dispatch( actionCreators.increment() ),
Then in our action we can recibe that parameter and return it.
export const add = (payload) => {
return {
type: ADD,
payload: payload // { value: 5 }
};
};
But to made it easer to read what we are sending we can show what values we are passing.
export const add = (payload) => {
return {
type: ADD,
payload: {
value: payload.value
}
};
};
In the end this actions will only call their respective reducers and this actions are a nice structure we can use as base for a more
complex state management like the Asynchronous one.
Redux Thunk is a library which adds a middleware to your project which allows our actions to not return the action itself but return a
function which will eventually dispatch an action. With this little trick, not returning the action itself but a function which will then
dispatch one, we can run asynchronous code because the eventually dispatched one part is the part which may run
asynchronously.
The ‘thunk action’ in the end dispatch the action that call its reducer, like this one.
export const saveResult = (payload) => {
return {
type: STORE_RESULT,
payload: {
result: payload.result
}
};
};
Instead of having a single file for our actions we can have many.
indexActions.js \
export {
add,
subtract,
increment,
decrement
} from './counterActions';
export {
storeResult,
deleteResult
} from './resultActions';
actionTypes.js \
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const ADD = 'ADD';
export const SUBTRACT = 'SUBTRACT';
export const STORE_RESULT = 'STORE_RESULT';
export const DELETE_RESULT = 'DELETE_RESULT';
resultActions.js \
import * as actionTypes from './actionsTypes';
redux-thunk can pass as an additional argument, getState, that is a method we can execute to get the current state. Sometimes in
your asynchronous code, you need to be able to reach out to the state prior to your to-be-dispatched action, let's say you want to
save some data for a given user and you have the id of the user stored in your redux state, you can then get it with getState.
In this example we show our storeResult action using the second parameter that Redux-thunk functions gave us to simply log a
value from our state, of course we can use it do much more complex functions.
resultActions.js \
export const storeResult = (payload) => {
return (dispatch, getState) => {
setTimeout(() => {
const oldCounter = getState().counterState.counter;
console.log(oldCounter);
dispatch(saveResult(payload))
}, 2000);
}
}
try to write action creators and reducers in a way that you don't have to use getState, instead you can pass all the data you need in
your async action creator like the user id into it by accepting it as an argument.
Routing
The idea behind a single page application is to have, well guess what?
A single page, a single html file. We still want to provide the user with a normal web using experience, but in a SPA we actually don't
have have multiple html files, but then we instead use javascript to render different pages for different paths.
So we don't really have different files but simply we re-render parts off that single page or maybe
the entire single page depending on which path the user navigated to in our application. This is what
routing is about, parsing this path, so the path after our domain and showing the appropriate jsx or component code in our app.
And for that as I said, we're going to use a router package.
The router package first of all of course it has to parse the URL path to understand where the user wanted to go to.
Then we as a developer have to configure different paths in our application which we support and the router package can then read
our configuration basically, so that it knows which paths are supported and what should happen when the user visits one of these
paths.
In the third step, it will then render or load the appropriate jsx or component code depending on which path the user visited.
This is the idea behind routing, load different code, conditional jsx or component code for different paths and we use a router
package so that we don't have to determine which path the user is on on our own.
.Blog li {
display: inline-block;
margin: 20px;
}
.Blog a {
text-decoration: none;
color: black;
}
We need to instal this two packages react-router and react-router-dom. this is a packages are not created by Facebook but it's
de-facto standard for routing in react applications.
react-router contains the logic you could say but to be able to render something to the dom, so to tell react to render something, we
need react-router-dom too.
$ npm install --save react-router react-router-dom
We installed both react-router and react-router-dom . Technically, only react-router-dom is required for web development. It wraps
react-router and therefore uses it as a dependency.
We don't need to install react-router on our own for it to work. You can omit this installation step, I left it in there for historic reasons
and because I like to emphasize that the main package is named react-router. If you ever search for assistance, you probably want
to search for "react router" - that's the name of the package.
An structure we you have a main container (Blog) that call the other containers is ideal.
Our route component needs a path property which is a simple string and if that string matches our current route, route has a
property called render which actually holds a reference to a function and you can pass an anonymous function, an arrow function
there. This function then has to return jsx which should be rendered.
<Route path="/" render={() => {
return (<h1>HI</h1>);
}} />
But even if we are not in our home path, its JSX will be render!
That is so because react router to determine which path you're on sees if your current path starts with this path. So if this is a
prefix, you can override this behavior by adding one more prop which is exact.
<Route path="/" exact render={() => {
return (<h1>HI</h1>);
}} />
We can as many route components as we want and they can vary in configuration.
<Route path="/" exact render={() => {
return (<h1>HI</h1>);
}} />
<Route path="/" render={() => {
return (<h1>HI 2</h1>);
}} />
We will get the following result on our browser.
8.6 - Rendering Components for Routes
We want to use the component property and as the name suggests, this allows us to pass a component which should be rendered
in this route's place.
In the end here this is the default case which will use a lot in our apps, we normally want to render full components.
with the Link component, and also replace our href property with the to property
<li><Link to="/">Home</Link></li>
<li><Link to="/new-post">New Post</Link></li>
to can also be a more complex element, it can be a javascript object and hence needs to be wrapped in single curly braces to
output dynamic content and then the dynamic content is a javascript object, so one more pair of curly braces.
Inside our object we can use the property pathname to set our pat.
<li><Link to={{ pathname: '/new-post' }}>New Post</Link></li>
We can add hash, this would allow us to then jump to any ID submit we have in that elements, so using that feature of appending a
fragment of the URL, you might be aware of that, you can simply add hash something after the URL to jump to that point.
<li><Link to={{
pathname: '/new-post',
hash: '#submit'
}}>New Post</Link></li>
We can also set up search and search allows us to add queryParams like quick submit equals.
<li><Link to={{
pathname: '/new-post',
hash: '#submit',
search: '?quick-submit=true'
}}>New Post</Link></li>
<header>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to={{
pathname: '/new-post',
hash: '#submit',
search: '?quick-submit=true'
}}>New Post</Link></li>
</ul>
</nav>
</header>
react router gives us some extra information about the loaded route through props.
componentDidMount() {
console.log('[NewPost]',this.props);
}
history, location and match, is not something we set up, this is passed by react router.
To add a class to our links we actually need to use another component instead of link, we use NavLink
Blog.js \
import { Route, NavLink } from 'react-router-dom';
Sometimes you don't want to use that default of active as an active class name and for that we can use the property
activeClassName. which uses the following syntax: activeClassName=”<class name>”.
<li><NavLink to="/" exact activeClassName="my-active-link" >Home</NavLink></li>
For passing route parameters we define a flexible, a variable route parameter with colon and then any name of your choice, the
syntax goes as following path=”/:<parameter name>”.
Blog.js \
<Route path="/:id" component={FullPost} />
This will be replaced dynamically or essentially it tells the react router ‘hey whenever you have slash something, this route here is
meant’ so in this example just call an ID.
Post.js \
import { Link } from 'react-router-dom';
Basically we can add a dynamic link wrapping inside parentheses a dynamic value.
<Link to={'/' + post.id} key={post.id} >
return (
<section className="Posts">
{posts}
</section>
);
In our app, till this point we can see that we successfully created dynamic links, but we need to know how to extract those values.
We can log the props off our component with our lifecycle componentDidMount
Post.js \
componentDidMount () {
console.log('[FullPost]',this.props);
}
Taking a log to our object we can see that we can get our id inside the route props.match.params.id.
Our general structure will look the following way, and we are using this values here to create a dynamic petition.
componentDidMount () {
console.log('[FullPost]',this.props.match.params.id);
if ( this.props.match.params.id ) {
if ( !this.state.loadedPost || (this.state.loadedPost && this.state.loadedPost.id !== this.props.id) ) {
axios.get( '/posts/' + this.props.match.params.id )
.then( response => {
// console.log(response);
this.setState( { loadedPost: response.data } );
} );
}
}
}
Testing
What is testing? testing does not mean that we test our application manually, we obviously do that and we should do that. With
testing here, I mean that we write automated tests.
So what we typically do is we build an application, then of course we test it manually in the browser and then we ship it to a server,
now there's one extra step we can add in any development workflow and that is that we don't just ship the app after testing it
manually but that we also have automated tests.
Now these are tests which run automatically, we write them and then each test tests a tiny fraction of
our application,that's a so-called unit test,it tests a unit of our app.
9.2 - Required testing tools
The first tool is the test runner,this is basically a tool which is responsible for executing our tests so for running the code, test code
and providing a validation library which in the end is a library which allows us to do comparisons and potentially throw some errors.
The idea behind unit tests is that they don't run in the browser but instead with nodeJS though often emulated to be in a browser
environment with the help of specific javascript packages.
the good thing is create react app already comes with a pre-configured testing environment, Jest is already installed in the app that
is the one we will use as test runner.
When working with reacting components, we also need a way of emulating these component, for this, we need testing utilities
which help us with testing and there, we specifically need the just described help to simulate the react app, mount components and
dig into that dom which is created with react.
And that's Enzyme. Enzyme is a tool developed by AirBnB, they use react in a lot of their projects and they share this tool which
makes it easy to mount components and then navigate through them.
Jest
In general, here are some things you would not want to test: you don't want to test the library,
you don't want to test react or axios or redux itself, these are third party libraries which already were tested by the developers.
you want to test the code you added to your application and there you want to test the code which does not use that library.
Additionally you don't want to test too complex connections especially in react, there is the danger of testing that you click a button
in one component and you change something in a totally different component.
Typical examples are isolated units,you want to test that reducer function you created, you want to test that component function
you created, you also want to test conditional output if your component has a property which leads to something being rendered if
that property is true, then you want to test if this really happens.
The first thing is that we need to install enzyme and then the react test renderer package, that is a dependency of enzyme which we
need to install separately and we need to install an adapter of the enzyme package to our current react version. So here this is the
enzyme adapter-react-16 version 4 react 16.
// Functions //
const parseLatLong = (string) => {
let parsedString = string.split(',');
let lat = parseInt(parsedString[0]);
let long = parseInt(parsedString[1]);
return `${lat}, ${long}`;
}
if (Object.entries(data).length > 0) {
let currentDay = formatDataToBeCompared();
let currentDayWeatherObject = data.consolidated_weather[getInfoDependingOfDay(currentDay, data)];
renderData = (
<React.Fragment>
<div className={css.citySection}>
<p> {`${data.title}, ${data.parent.title}`} </p>
<p> {parseLatLong(data.latt_long)} </p>
</div>
<div className={css.imageSection}>
<img
src={`https://www.metaweather.com/static/img/weather/png/64/${currentDayWeatherObject.weather_state_abbr}.png`}
alt="Weather PNG"
/>
</div>
<div className={css.weatherSection}>
<p> {currentDayWeatherObject.weather_state_name} </p>
<p> {`${parseInt(currentDayWeatherObject.min_temp)} ℃-${parseInt(currentDayWeatherObject.max_temp)} ℃`} </p>
</div>
</React.Fragment>
);
}
// Rendering //
return (
<div className={css.flexContainer}>
<h2>Weather Forecast</h2>
{showCardStatus()}
</div>
);
}
Now inside this test file, I now can start writing my test and a test uses Jest by default and Jest on
the other hand gives us a couple of methods to define the test.
The first important method is the describe method,you don't need to import it, Describe is a function that takes two arguments, the
first is just a description of the test bundle.This is only what you'll see later in the console output so it should be something which
allows you to identify which kind of tests we'll run here.
WeatherCard.test.js \
describe('<WeatherCard />', …);
The more interesting part comes in a second argument, that is your testing function.
next thing is to write the ‘it’ function, It describes or allows you to write one individual test, it also takes two arguments.
The first one is again just a string, a description which will appear in the console
it('If <WeatherCard /> has props.status === "Loading" it should render a div with class "classloadingSection"', …);
We want to create an instance of one component as it would be rendered to the DOM, so we need to import two things form
enzyme:
import { configure, shallow } from 'enzyme';
and our adapter:
import Adapter from 'enzyme-adapter-react-16';
because we are going to use JSX with a method called shallow we need to import react.
import React from 'react';
We can now execute configure and pass a javascript object to configure. There we should set up an adapter property and assign
new adapter as a constructor function, so this adapter is instantiated with new adapter and that's all, with that enzyme is
connected.
configure({ adapter: new Adapter() });
So far it was only boilerplate and strings, now we are going to use shallow, Shallow is the most popular or the best way of rendering
react components in many circumstances.
because one thing shallow does is it renders the component with all its content but the content isn't deeply rendered,we don't then
render a whole subtree of components, we just want to render a component and know what's inside of it without rendering
everything which is nested inside its included components.
We can use static objects to fill those properties instead of data from a petition.
import {weatherDataObject} from '../../test_objects/testObjects';
we can simply add those values to our properties inside our shallow function.
const wrapper = shallow(<WeatherCard data={weatherDataObject} status="Loading" />);
The last part of testing is when we write our expectation, for that we use the expect method, Inside expect, we define the thing we
want to check.
Here we are expecting to find a div with the class loadingSection and with toHaveLength we establish that we expect a certain
number of items we define in the find method, in this case we expect only one.
expect( wrapper.find('div.loadingSection')).toHaveLength(1);
// Local Imports //
// Configurations //
// Tests //
beforeEach takes a function as an argument and this is the function which will get executed before each test, we can use it so we
don't the need to set our wrapper for each test.
let wrapper;
beforeEach( () => {
wrapper = shallow(<WeatherCard data={weatherDataObject}/>);
});
And to set different properties to each wrapper we can use the function setProps.
wrapper.setProps({ status: "Loading" });
// Local Imports //
// Configurations //
// Tests //
let wrapper;
beforeEach( () => {
wrapper = shallow(<WeatherCard data={weatherDataObject}/>);
});
it('If <WeatherCard /> has props.status === "Loading" it should render a div with class "classloadingSection"', () => {
wrapper.setProps({ status: "Loading" });
expect( wrapper.find('div.loadingSection')).toHaveLength(1);
});
it('If <WeatherCard /> has props.status === "Success" it should render a div with class "citySection"', () => {
wrapper.setProps({ status: "Success" });
expect( wrapper.find('div.citySection')).toHaveLength(1);
});
});
Jest:
https://jestjs.io/docs/en/getting-started
Enzyme:
https://airbnb.io/enzyme/docs/api/ShallowWrapper/prop.html
or
https://github.com/airbnb/enzyme/
Comparison Tables
11.1 - Table of equivalents of lifecycle methods between class-based components and functional components
componentWillMount(){
// Fires immediately before initial render
}
componentDidMount(){ useEffect()
/* Fires immediately after initial render, ideal for making useEffect takes two arguments:
petitions because setState is available */
} If our second argument is an empty array our useEffect will
behave like componentDidMount executing only once.
componentWillReceiveProps(){
// Fires when component is receiving props
}
shouldComponentUpdate() {
// Fires before rendering with new props or state
}
componentWillUpdate() {
/* Fires immediately before rendering with new props or state
*/
}
componentDidUpdate() { useEffect()
/* Fires immediately after rendering with new P or S */ useEffect takes two arguments:
}
If we add state elements in the array of our second argument, it
will execute whenever those values are changed thus acting
like componentDidUpdate.
componentWillUnmount(){
/* Fires immediately before component is unmounted from
DOM (removed) */
}