What is React? React is a declarative JavaScript library for building user interfaces. We are able to build complex UIs from small, isolated pieces of code called ‘components’. These components tell us what we want to see on the screen. React handles the update and re-render of components.
An example of a component is React.Component
subclasses:
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
// Example usage: <ShoppingList name="Mark" />
So what does this mean? ShoppingList
is a React component class (akak React component type).
A component takes in parameters (props
, aka ‘properties’) and returns a hierarchy of views to display
via the render
method.
The render
method returns a description of what you want to see on the screen. React takes this description
and displays the result, which is a React element (lightweight description of what to render).
React uses a special syntax called JSX
which makes these structures easier to write. The <div />
syntax
is transformed at build time to React.createElement('div')
. The example above turns into:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */),
React.createElement('ul', /* ... ul children ... */)
);
https://reactjs.org/docs/react-api.html
If you load React from a <script>
tag, then these top-level APIs are available on the React
global.
#ES6 with npm
import React from 'react'
#ES5 with npm
var React = require('react')
Google Chrome Extensions has a ‘React Developer Tools’ extension With this, in Chrome DevTools, you can click on the ‘Components’ section to look at React Components.
JSX is the syntax extension to JavaScript. We use JSX with React to describe what the UI should look like. JSX produces React ‘elements’. It looks like this:
const element = <h1>Hello world!</h1>
The idea is that React separates concerns with loosely coupled units called components.
const name = 'Will'
const element = <h1>Hello {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
You can put any type of JavaScript expressions inside the curly braces in JSX (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions)
Note: Recommend using the ‘Babel’ language definition for your editor for proper syntax highlighting For VSCode, that means installing the extension ‘vscode-language-babel’
Elements are the smallest building blocks for React apps. An element describes what you see on the screen. React DOM handles updating the DOM to match the React elements. React’s components are made up of a lot of elements.
const element = <h1>Hello world</h1>
We normally have a single ‘root’ DOM node because everything inside it will be managed by React DOM.
To render a React element into a root DOM node, pass both to ReactDOM.render()
, like:
# Say we have this in your HTML file
<div id="root"></div>
# call this to render element into DOM
const element = <h1>Hello world</h1>;
ReactDOM.render(element, document.getElementById('root'));
React elements are immutable; once you create the elmenet, you cannot changes its children or attributes.
The only way to update the UI is to create a new element and pass it to ReactDOM.render()
.
function tick() {
const element = (
<div>
<h1>Hello world</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000); # call the render every second
Even though we describe the whole UI tree each tick, React only updates the content that has changed (if any).
Components lets you split the UI into independent reusable pieces. Components can be defined as either
classes or functions (with classes providing more features). To define a React component, you need to
extend React.Component
. For reference, the component API is here: https://reactjs.org/docs/react-component.html
Here it is as a class
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>
}
}
The only method you have to define in a React.Component
sublcass is the render()
method.
The idea is that code reuse is primarily done through composition rather than inheritance.
Here it is as a function (aka function components)
function Welcome(props) {
return <h1>Hello {props.name}</h1>
}
This is a valid React component because it accepts a single ‘props’ (properties) object argument with data and returns a React element.
Elements can be:
See examples below:
# DOM tag
const element = <div />;
# user-defined component
const element = <Welcome name="Will" />;
When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object (aka ‘props’).
Here we render a ‘Hello Will’ on the page:
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
const element = <Welcome name="Will" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Here’s what is happening:
React.Render()
is called with the element <Welcome name="Will" />
Welcome
component with the properties {name: 'Will'}
as the propsWelcome
component returns a <h1>Hello Will</h1>
element as the result<h1>Hello Will</h1>
Note: Always start a component name with a capital letter. Starting with a lowercase letter means that this gets interpreted as a DOM tag instead of a component.
Your components can refer to other components in their output. This gives us the same component abstraction
for any level of deatil. Everything (a button, a form, a screen) are all expressed as components.
For this example, we have an App
component that renders multiple Welcome
components:
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
You can convert a function into a component class by calling it a class, adding a render()
method,
and replacing props
with this.props
like so:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello world</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
To add state, we add a class constructor that assigns the inital this.state
class Clock extends React.Component {
constructor(props) {
super(props); /* You always want to call the base constructor with props) */
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello world</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Instead of updating based off a time (e.g. every 1 second update DOM), we use lifecyle methods (see ‘Lifecycle’):
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
/* runs after the component output has been rendered to the DOM */
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
/* runs tear down code */
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
So what’s going on?
1 The <Clock />
component is passed to ReactDOM.render()
where React calls the constructor method.
2 The constructor
method initializes this.state
with an object (the current time)
3 React calls the Clock
component’s render()
method and React updates the DOM to match the render output
4 When the Clock
output is inserted into the DOM, React calls the componentDidMount()
lifecycle method
5 Inside this method, Clock
asks the browser to set up a timer to call the component’s tick()
method once a second
6 Each second the browser calls the tick()
method and the Clock
component schedules a UI update by calling
setState()
with an object containing the current time. The setState()
call lets React know that the
state has changed so it calls render()
method again to see what should be drawn (using the new this.state.date
)
7 If the Clock
component is removed from the DOM, then React calls componentWillUnmount()
method
Some rules about state:
Set state with setState()
this.state.comment = 'Something'
is wrong)setState()
function (e.g. this.setState({comment: 'Hello'});
)this.state
is in the constructorState updates may be asynchronous
setState()
calls into a single update for performancethis.props
and this.state
may be updated asynchronously so you should not rely on their values
for calculating the next statesetState()
that accepts a function rather than an object; that funtion will receive the
previous state as the first argument and the props at the time the update is applied as the second argumentFor example:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
State updates are merged
When you call setState()
, React merges the object you provide into the current state.
Data Flows Down
Parent and child components do not know (and do not need to know) if a component is stateful or stateless This is why state is called local or encapsulated. A component can pass its state down as props to its child components like this:
<FormattedDate date={this.state.date} />
The FormattedDate
component can receive the date
in its props and wouldn’t know whether it came from
the Clock
’s state, fromt he Clock
’s props, or was typed by hand.
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}</h2>;
}
Each component has several ‘lifecycle methods’ that you can override to run code at particular times in the process. The Lifecycle Cheat sheet is here: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ The main lifecycle methods are for:
These methods are called in this order when a component is created and inserted into the DOM:
constructor()
static getDerivedStateFromProps()
(not used often)render()
componentDidMount()
An update is caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:
static getDerivedStateFromProps()
(not used often)shouldComponentUpdate()
(not used often)render()
getSnapshotBeforeUpdate()
(not used often)componentDidUpdate()
This method is called when a component is being removed from the DOM:
componentWillUnmount()
The render()
method is the only required method in a class component.
When called, it should examine this.props
and this.state
and return one of the following types:
<div />
or <mycomponent />
Renders should not modify component state and should return the same results each time it’s invoked
Handling events with React elements is similar to handling events on DOM elements.
Say we have this HTML
<button onClick="activateLasers()">
Activate Lasers
</button>
In React, this would be:
<button onClick="{activateLasers}>
Activate Lasers
</button>
In React, to prevent default behavior, you cannot return false
. Instead, you have to
call preventDefault
explicitly. In HTML this might look like:
<a href="#" onclick="console.log('The link was clicked'); return false">
Click me
</a>
In React this would be:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked');
}
return (
<a href="#" onClick{handleClick}>
Click me
</a>
);
}
With React, you do not need to call addEventListener
to add listeners to a DOM element after it is created.
Instead, provide a listener when the element is initially rendered. A common pattern is for an event handler
to be a method on the class. For example, the Toggle
component renders a button that lets the user toggle
between ‘ON’ and ‘OFF’ states:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// this binding is necessary to make 'this' work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
You can pass extra parameters to an event handler. Either of these would work to pass in an additional id
param.
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
These two are the same, just one uses arrow functions and the other uses Function.prototype.bind
.
The e
argument representing the React event will be passed as a second argument after the ID.
With an arrow function, we explicitly pass e
. With bind
, any additional arguments are automatically forwarded.
In React, you can create distinct components that encapsulate behavior you need. You can then
render only some of them depending on the state of your application. Conditionals in React are the same
as conditions in Javascript, use an if
or a conditional (ternary) operator to create elements
representing the current state (and let React update the UI).
function UserGreeting(props) {
return <h1>Welcome back!</h1>
}
function GuestGreeting(props) {
return <h1>Please sign up</h1>
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);