# Modern React and Redux
AirBnB convention (opens new window)
# 1. Dive in
# First Look
App function is a React Component
Component have two jobs.
- return JSX
- handles events
All components return JSX : instructions that tell React what to show on screen. Looks similar to HTML.
JSX elements
- Return HTML element
- Return another component
React iterates over every element in JSX.
Check if DOM element. Yes? Show on screen.
No? Call the component function and iterate through it again.
ReactDOM.render(<App />, document.getElementById("root"))
First argument : get App function, get JSX, render into HTML
Second argument : Then take the HTML and place it inside the div with this ID in index.html.
The two libraries.
React : Reconciler. Gets components, JSX and iterate through it to make HTML or get more components.
ReactDOM : Renderer. Actually create HTML and put it to DOM.
useState : Works with React's state system. Used to update contents on the screen.
# Generating a React Project
npx create-react-app myapp
npm init react-app my-app
yarn create react-app my-app
# npm vs npx vs yarn
npx allows us to execute it quick.
# What is create-react-app?
Install over 1782 packages. Most importantly, Babel, Webpack and Dev server.
Babel : Since 2015, ES is upgraded and new syntax are made. However, browsers' support for newer ES is poor. Babel changes newer version of ES into largely supported version.
Webpack:
Dev Server :
# Project Directory
# JSX
React works by creating JSX files. Having HTML right inside of JS files. Compiler, which is known as Babel, changes the JSX into a plain Javascript.
Babel changes JSX into HTML using React.createElement functions
# Template literals = String interpolation
<h1>Hello {`${fName} ${lName}`}</h1>
Outer bracket means JS in JSX.
Backtick means template literals, meaning these will be strings.
Inner bracket means JS inside template literals.
# Component
// Import the React and ReactDOM libraries
import React from "react";
import ReactDOM from "react-dom";
// Create a React component
const App = function () {
return (
<div>
<label htmlFor="name" className="label">Enter name:</label>
<input type="text" id="name" />
<button style="background-color:blue; color:white;"></button>
</div>
)
// Take the React component and show on screen
ReactDOM.render(<App />, document.querySelector("#root"));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- You MUST write component in the same line as return
- If you don't there will be an error.
- Common practice is to write parenthesis on the first line.
# Inline styling with JSX
Outer parenthesis declares we want to reference JS variable.
Second parenthesis declares JS object.
Any kebab casing turns into camel casing.
value is enclosed with quote marks
semicolons are removed and comma is used. Because this is a JS object.
Convention demands use double quotes with JSX properties. None JSX property should use single quotes. Lots of companies use double quotes everywhere. Your choice.
# Class with JSX
The keyword 'class' is reserved for JS class in JSX. We have to use className instead. There is a discuss about removing className and just using class because React is becoming smarter.
# JSX can reference JS variables
const App = function () {
const buttonText = "Click Me!";
return (
<div>
<button>
{buttonText}
</button>
</div>
);
};
2
3
4
5
6
7
8
9
10
11
{buttonText}
the parenthesis tells 'I want to use a JS variable'.
Limitation
It's ok to put
strings
,integers
,arrays
to the JS variable.But if you put
object
, you will have an error:
function getButtonText(){
return 'Click on me!'
}
const App = function () {
return (
<div>
<button>
{getButtonText()}
</button>
</div>
);
};
2
3
4
5
6
7
8
9
10
11
12
13
Function can be used instead as well.
# 3. Communicating with Props
React components are written in 'ReactComponent' style. Not kebab, snake, or camel. Every component in React is independent. It must be export
ed and import
ed for 'component nesting'.
// CommentDetail.js
export default CommentDetail;
// index.js
import CommentDetail from './CommentDetail'
<CommentDetail />
2
3
4
5
6
- In relative path,
./
states the current folder. - Components don't use curly braces. Treated as JSX tag.
- Props : System for passing data from a parent component to child component.
// property name = "value"
<CommentDetail author="Coco" />
2
const CommentDetail = (props)=>{
console.log(props)
//{author:"coco"}
return(
<div>
{props.author}
</div>
)
}
2
3
4
5
6
7
8
9
# html attribute vs react props
attributes are predefined, props are customizable
key
is a special property and does not act as props.
# Prop to prop
<ApprovalCard>
<CommentDetail
author="Keith"
time="Today at 15:01"
content="yeah"
avatar={faker.image.image()}
/>
</ApprovalCard>
2
3
4
5
6
7
8
9
const ApprovalCard = (props)=>{
return (
<div>
{props.children}
</div>
)
}
2
3
4
5
6
7
8
- You can put prop inside a prop.
- It will be sent down as
props.children
. Everything between the tag will be represented asprops.childern
.
# mapping object to props
const contacts = [
{
name: "Beyonce",
img:
"https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg",
phone: "+123 456 789",
email: "b@beyonce.com"
},
{
name: "Jack Bauer",
img:
"https://pbs.twimg.com/profile_images/625247595825246208/X3XLea04_400x400.jpg",
phone: "+123 456 789",
email: "jack@nowhere.com"
},
{
name: "Chuck Norris",
img:
"https://i.pinimg.com/originals/e3/94/47/e39447de921955826b1e498ccf9a39af.png",
phone: "+123 456 789",
email: "gmail@chucknorris.com"
}
];
const rendered_contacts = contacts.map((contact) => {
return (
<Card
name={contact.name}
img={contact.img}
phone={contact.phone}
email={contact.email}
/>
);
});
ReactDOM.render(
<div>{rendered_contacts}</div>)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 4. Structuring Apps with Class-based Components
# React in the Past
Functional components used to produce JSX to show content to user.
Class components were used to produce JSX to show content to user & use lifecycle method to run code at specific points & use state system to update content on screen
Functional components were much more restricted. But now it became more dynamic
Which one should we use?
Companies with established projects are using class-based components because this used to be the way.
Newer projects use either one.
You must understand both.
Learn class-based -> function-based -> redux
# Geolocation API
window.navigator.geolocation.getCurrentPosition(successCallback(){}, failureCallback(){})
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position) ,
(err) => console.log(err)
)
2
3
4
5
6
# Lifecycle of functional component
Getting geolocation service takes some time because it uses API.
Problem is when we get result of geolocation, the screen is already rendered!
There must be a way to 'pause' or 'rerender' the updated part.
Class component must be used to update screen.
# Rule of Class components
- Must be a Javascript Class. Different from Ruby or Java's object oriented class inheritance. JS uses prototypal inheritance. But works like class in this case
- Must extend React.component
- Must define render method that returns JSX
class App extends React.Component{
render() {
return <div> Latitude: </div>
}
}
2
3
4
5
When class
is made, JS class only has render
method. React expects many more methods and these are implemented in React.Component
. By using extends
we are creating a sub-class.
# State in React Components
Using class component is not enough to rerender updated screen.
- Understanding state will open new understandings
- But it is difficult and challenging.
# Rules of state
Only usable with class components. (can be used with functional using the hooks system)
It's confusing with props. Props vs States
State is a JS object that contains data strictly relevant to a component.
Updating state causes rerendering of the componenet.
State must be initialized when component is created.
State can only be updated using the function
setState
.
# Initializing
class App extends React.Component{
constructor(props){
super(props)
this.state = {lat: null}
}
render() {
return <div> Latitude: {this.state.lat} </div>
}
}
2
3
4
5
6
7
8
9
10
11
- Constructor is specific to JS not only React. Any time a new instance is created, constructor function is instantly called before anything else. This makes it a good place to initializing state.
- The
super
method is called fromReact.Component
. We are replacing the javascript constructor method with react'sconstructor
method. To make sureReact.Component
'sconstructor
method is called, we statesuper(props)
.
# setState
// right way
this.setState({ lat: position.coords.latitude });
// NEVER DO THIS. Only exception is when we initialize.
this.state.lat = position.coords.latitude
2
3
4
5
this.state
direct assignment is only used on initialization
# App lifecycle walkthrough
What happens?
- JS file loaded by browser
- Instance of App component created
- Constructor function gets called. (super needed)
- Initialized state object with properties of interest.
this.state
is a very special name and cannot be named else. - Call async API. The callback function is not executed until some time in the future but the constructor finishes before.
Render
method executed.- App returns JSX and rendered to HTML.
- API request responsed.
this.setState
updates our state- React checks update and runs
render
method one more time. - Updated JSX returned.
- Screen updated.
App component is rendered twice.
# Handling errors (with grace)
For better UX
render() {
if (this.state.errorMessage && !this.state.lat) {
return <div> Error: {this.state.errorMessage} </div>;
}
if (!this.state.errorMessage && this.state.lat) {
return <div>Latitude : {this.state.lat} </div>;
}
return <div>Loading!</div>;
}
2
3
4
5
6
7
8
9
10
- conditional rendering.
# componentDidMount
When I refresh too quick repeatedly, I get
# 6. Lifecycle Methods
We've already seen constructor function. We've also seen render method. Render method is not optional unlike others. Let's see what other methods there are
// Called one time after rendered
componentDidMount(){}
// Called each time updated
componentDidUpdate(){}
2
3
4
5
After placing window.navigator.geolocation.getCurrentPosition
in the componentDidMount
, the error
went away.
Do not do dataloading in constructor. setState can only happen to components that are mounted.
# Alternate way to initialize state
constructor(props) {
super(props);
this.state = { lat: null, errorMessage: "" };
}
// Same as below. Babel will build up the constructor for us.
state = { lat: null, errorMessage: '' };
2
3
4
5
6
7
This makes it simpler. Since constructor
method is only used to initialize states, this is a good way to do it.
# Expression vs Statement
An expression evaluates to a value. A statement does something. Statements represent an action or command e.g print statements, assignment statements. Expression is a combination of variables, operations and values that yields a result value.
- Statement :
if
switch
statements that to conditional stuffs. - Expression : simple return value.
# JSX {} can only contain expressions, not statements
function App(){
return <div>
{if(isLoggedIn ===true){
return <h1> Hellow</h1>;
}else{
return <Login />
}}
</div>
}
2
3
4
5
6
7
8
9
- This does not work because statement is inside {}.
# JS ternary operator(inline condition)
CONDITION ? DO IF TRUE : DO IF FALSE
- By using ternary operator, the
if else
statement changed into expressions.
//getting the current season
function App(){
return <div>
{isLoggedIn === true ? <h1>Hello</h1> : <Login/>}
</div>
}
2
3
4
5
6
7
8
# semantic-ui icons
const icon = season === "winter" ? "snowflake" : "sun";
<i className={`${icon} icon`}></i>
2
# configuring options and destructuring
const seasonConfig = {
summer: {
text: 'let\'s hit the beach',
iconName: 'summer'
},
winter: {
text: 'burr it\'s chilly',
iconName: 'winter'
}
}
.
.
.
const season = getSeason(props.lat, new Date().getMonth());
//deestructuring
const { text, iconName} = seasonConfig[season]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# importing CSS in React
import './SeasonDisplay.css'
- Webpack will see the the import, and stick it to
index.html
.
# Default Props
// First way
const AppName = (props)=>{
return (
{props.message || 'Loading'}
)
}
// The beautiful way
AppName.defaultProps = {
message:"Loading..."
}
2
3
4
5
6
7
8
9
10
11
12
- Great to make reusable elements.
# props combined with ternary expressions
//App.js
import React from "react";
import Form from "./Form";
var userIsRegistered = false;
function App() {
return (
<div className="container">
<Form userIsRegistered={userIsRegistered} />
</div>
);
}
export default App;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Form.js
import React from "react";
function Form(props) {
return (
<form className="form">
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
{!props.userIsRegistered && (
<input type="password" placeholder="Confirm Password" />
)}
<button type="submit">
{props.userIsRegistered ? "Login" : "Register"}
</button>
</form>
);
}
export default Form;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Summary of Class Components
+State can only be updated using the function setState
# Clock
class Clock extends React.Component {
state ={time:new Date().toLocaleTimeString()}
componentDidMount() {
setInterval(() => {
this.setState({time: new Date().toLocaleTimeString()})
}, 1000)
}
render() {
return (
<div className="time">
The time is: {this.state.time}
</div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Hoisting
This should be on JS md. Move later
- Hoisting means getting assigned before it should happen.
console.log(animal)
var animal = "Coco"
console.log(animal)
// Undefined
// Coco
console.log(animal)
let animal = "Coco"
console.log(animal)
// ReferenceError : cannot access before initiaization
2
3
4
5
6
7
8
9
10
11
- In the first case, JS knew that
animal
would be declared before actually it was. This is called hoisting and happens tovar
- It can also happen to functions.
howl();
function howl(){
console.log('woof')
}
// woof
hoot();
const hoot = function(){
console.log('hoo hoo')
}
// ReferenceError : cannot access 'hoot' before initialization
hoot();
var hoot = function(){
console.log('hoo hoo')
}
console.log(hoot)
// TypeError : hoot is not a function (Type is Undefined)
// (if executed) Undefined
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- function declarations are hoisted. These can thought that they are placed on the top of the JS file.
- function expressions are not hoisted.
# 7. Handling User Input with Forms and Events
# onChange
class SearchBar extends React.Component {
onInputChange(event) {
console.log(event.target.value);
}
render() {
return (
<div>
<form>
<input
type="text"
onChange={this.onInputChange}
/>
</form>
</div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
onChange
,onClick
andonSubmit
are special names.onChange={this.onInputChange}
NotonChange={this.onInputChange()}
Because we do not want to execute it everytime its rendered. Its a reference of callback function so we can use it sometime in the future.
# onChange alternate: inline arrow function callback
<input type="text" onChange={(event)=>{console.log(event.target.value)}} />
- Can use anonymous arrow function instead of separate function
# Controlled vs Uncontrolled element
What we did before was uncontrolled element. Below is controlled element made by using state
.
class SearchBar extends React.Component {
state = {term:""}
render() {
return (
<div>
<form>
<input
type="text" value = {this.state.term}
onChange=(e)=>{this.setState({term:e.target.value})}
/>
</form>
</div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
state
is made and it's synchronized with theinput
with thevalue
property.- Because it uses
setState
component is rerendered everytime. - This is better because information is stored inside the React area not in the html & DOM area.
- This makes certain manipulations very easy.
this.setState({ term: e.target.value.toUpperCase() });
# this
class Car {
setSound(sound){
this.sound = sound
}
drive(){
return this.sound;
}
}
const car = new Car();
car.setSound('vroom')
const truck = {
sound: 'putput'
driveMyTruck:car.drive
}
truck.driveMyTruck()
//putput
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
driveMyTruck()
callscar.drive
.car.drive
callsthis.sound
. At this point, althoughthis
wascar
for car, becausethis
is being called fromtruck
,this
is assigned totruck
.this
is not assigned when it's being made but when it's being executed.
# binding this
Legacy way
class Car {
constructor(){
this.drive = this.drive.bind(this)
}
}
2
3
4
5
Using arrow function
onFormSubmit(e){
this.setState({term:e.target.value})
}
// upper is shorthand notation for below
onFormSubmit = function(e){
this.setState({term:e.target.value})
}
// We can use arrow function
onFormSubmit = (e)=>{
this.setState({term:e.target.value})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- arrow function do not default
this
to the window scope, rather they execute in the scope - arrow function does not have its own
this
. Thethis
value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching forthis
which is not present in the current scope, an arrow function ends up finding thethis
from its enclosing scope.
Final method : Using anonymous arrow function inline.
onChange={(e) => {this.setState({ term:e.target.value });
}}
2
# Communicating Child to Parent
//App.js
class App extends React.Component {
onSearchSubmit(term) {
console.log(term)
}
render() {
return (
<div>
//onSubmit is NOT a specific name. Changable.
<SearchBar onSubmit={this.onSearchSubmit}/>
</div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
///SearchBar.js
state = { term: "" };
onFormSubmit = (event) => {
event.preventDefault();
this.props.onSubmit(this.state.term)
}
2
3
4
5
6
7
this.props.
is used to get the passed down props function.- parent function is passed down as props. Searched term is sent to the parent function. Function is run at parent component.
# 8. Making API requests
Sending request to unsplash API
# Axios vs Fetch
yarn add axios
Axios
is 3rd party package, and fetch
is built into browsers. Axios can be very easily installed using npm. Fetch is far more basic and lower level. You will have to write lot of code that is already written in axios.
Highly recommend to use axios
.
// 3rd party import is placed above my components
import React from "react";
import axios from "axios";
import SearchBar from "./SearchBar";
class App extends React.Component {
onSearchSubmit(term) {
axios.get(`https://api.unsplash.com/search/photos`, {
params: { query: term },
headers: {
Authorization: "Client-ID kdfkldsljfrMYIDlsjfklsdc",
},
});}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Request with Async and Await
Two methods. async is newer and better
then()
A promise object is always returned after axios use.then()
function to chain. Anytime you are working with promise, you can use then
.
axios.get(`https://api.unsplash.com/search/photos`, {
params: { query: term },
headers: {
Authorization: "Client-ID KBHNjiOkkI7vlg-CmyRoIy29kQcZ37eTv0EoC0sgIzc",
},
})
.then((response) => {
console.log(response.data.results);;
2
3
4
5
6
7
8
async await
state = { images: [] };
async onSearchSubmit(term) {
const response = await axios.get(`https://api.unsplash.com/search/photos`, {
params: { query: term },
headers: {
Authorization: "Client-ID KBHNjiOkkI7vlg-CmyRoIy29kQcZ37eTv0EoC0sgIzc",
},
});
console.log(response)
this.setState({ images: response.data.results });
}
2
3
4
5
6
7
8
9
10
11
# Binding Callbacks
This error occurs because this
in this.state
does not point to the App class
. It instead points to the props
of SearchBar.
onSubmit
is sent down to SearchBar component as props. It is then executed via this.props.onSubmit(this.state.term)
. onSubmit
is executed on props
so the this
directs to the props
of SearchBar component.
Refactored as arrow function to fix the this
context.
onSearchSubmit = async (term) => {
const response = await axios.get(`https://api.unsplash.com/search/photos`, {
params: { query: term },
headers: {
Authorization: "Client-ID KBHNjiOkkI7vlg-CmyRoIy29kQcZ37eTv0EoC0sgIzc",
},
});
this.setState({ images: response.data.results });
}
2
3
4
5
6
7
8
9
10
# Creating Custom Clients
//unsplash.js
import axios from "axios";
export default axios.create({
baseURL: "https://api.unsplash.com",
headers: {
Authorization: "Client-ID KBHNjMYIDzc",
},
});
2
3
4
5
6
7
8
9
10
11
//App.js
import unsplash from "../api/unsplash";
onSearchSubmit = async (term) => {
const response = await unsplash.get(`/search/photos`, {
params: { query: term },
});
this.setState({ images: response.data.results });
};
2
3
4
5
6
7
8
9
# 9. Building Lists of Records
# map function in javascript
const numbers = [0, 1, 2, 3, 4];
let newNumbers=[]
const new_numbers = numbers.map((num)=>{
return num * 10
})
2
3
4
5
6
7
# Getting lists from props
const ImageList = (props) => {
const images = props.images.map((image) => {
return <img src={image.urls.regular} alt={image.alt_description}></img>;
});
return <div>{images}</div>;
};
2
3
4
5
6
# Keys in props
Purpose
React uses key to identify which list is updated by comparing the keys and the value.
Implementation
<img key={image.id} src={image.urls.regular} alt={image.alt_description} />
destructuring
const images = props.images.map((image) => {
return (
<img key={image.id} src={image.urls.regular} alt=image.alt_description}
/>
);
});
// destructured
const images = props.images.map(({id, urls, alt_description}) => {
return (
<img key={id} src={urls.regular} alt=alt_description}
/>
);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Accessing the DOM with Refs
Vanila JS : document.querySelector('img').clientHeight
React Refs gives access to a single DOM element. We create refs in the constructor, assign them to instance variables, then pass to a particular JSX element as props.
constructor(props) {
super(props);
this.imageRef = React.createRef();
}
.
.
<img ref={this.imageRef} src={urls.regular} alt={description} />
.
.
2
3
4
5
6
7
8
9
Listening until images are loaded
componentDidMount() {
this.imageRef.current.addEventListener("load", this.setSpans);
}
2
3
# Redux vs Vuex
Flux libraries are like glasses : you'll know when you need them -Dan Abramov
In the past it was not uncommon to have pieces of state across our application tucked inside of controllers, services, routes, directives (AngularJS), local storage, session storage, cookies, and some other alternatives.
When the application grows, this approach is really hard to scale and this is where Flux and React stepped in.
Flux is the architectural pattern on which the Redux and later Vuex (with some diffs) were based. Flux assumes unidirectional data flow, while MVC is based on bidirectional flow.
An Action consists of action type and eventually action payload, that is the data that will be propagated to the store.
Dispatcher takes an action and dispatches it to a store, which updates themself and propagates a change event to views.
The Store is a “single source of truth” - it’s a global state of our application. It's the central point of the Flux pattern, where data are stored and passed to all components.
View is usually the component
The main difference between them - while Redux uses reducers Vuex uses mutations. In Redux state is always immutable, while in Vuex committing mutation by the store is the only way to change data
React is different from Vue in the way it processes updates: React renders a virtual DOM then calculates optimal DOM operations to make the currently rendered DOM match the new Virtual Dom. But it has no way of knowing whether a particular component needs to re-render or not based on the new data. Vue instances keep track of which bits of data they depend on to render. These instances automatically register what needs to re-render when the data changes.