React JS
React JS
React is a Javascript library for building user interfaces. At its core, React provides a declarative library that keeps the DOM in sync with data.
Installation
Website
These details are valid on 3/7/2020
Step 1: Add a DOM container to the HTML
<!-- ... existing HTML ... --><div id="like_button_container"></div><!-- ... existing HTML ... -->
Step 2: Add the Script Tags
Add 3 script tags to the HTML page right before the closing of body tag.
<!-- ... other HTML ... --><!-- Load React. --><!-- Note: when deploying, replace "development.js" with "production.min.js". --><script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script><script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script><!-- Load our React component. --><script src="like_button.js"></script></body>
Step 3: Create a React Component
Create a file name like_button.js next to the HTML page. Content of this file is as follows:
'use strict';const e = React.createElement;class LikeButton extends React.Component {constructor(props) {super(props);this.state = { liked: false };}render() {if (this.state.liked) {return 'You liked this.';}return e('button',{ onClick: () => this.setState({ liked: true }) },'Like');}}const domContainer = document.querySelector('#like_button_container');ReactDOM.render(e(LikeButton), domContainer);
Using Toolchains
React App development can be supported by integrated toolchains. These toolchains help with the follow tasks:
- Scalling to many files and components
- Using third-party libraries from npm
- Detecting common mistakes early
- Live-editing CSS and JS in development
- Optimizing the output for production
Elements of a JS toolchain:
- Package manager (Yarn, npm)
- Bundler (Webpack, Parcel) to bundle modular node into small packages to optimize load time.
- Compile (Babel) to compile modern JS code for older browsers, and support new features such as JSX used by React.
Create React App (CRA)
CRA is recommended for learning React and building new single-page applications in React.
A single-page application is an application that loads a single HTML page with all necessary assets, such as JavaScript and CSS. Any interactions with the page or subsequent pages to no require a round trip to the server, which means the page is not reloaded.
npx create-react-app my-appcd my-appnpm start
Next.js
Next.js is a lightweight framework for static and server-rendered applications built with React. It includes styling and routing solutions out of the box and assumes that we use Node.js as the server environment.
Gatsby
Gatsby is a framework for creating static websites with React. It lets us use React components, but outputs pre-rendered HTML and CSS to guarantee the fastest load time.
CDN Links
For development:
<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>
For production:
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
Main Concepts of React
JSX
JSX is a syntax extension to JavaScript, used by React to describe what the UI component should look like. Technically, each JSX creates a React element (object?). After compilation by Babel, JSX becomes regular function calls to React.createElement(), which evaluate to JS objects.
Expressions can be embedded in JSX using curly braces.
function formatName(user) {return user.firstName + ' ' + user.lastName;}const user = {firstName: 'Harper',lastName: 'Perez'};const element = (<h1>Hello, {formatName(user)}!</h1>);ReactDOM.render(element,document.getElementById('root'));
Rendering Elements
An element describes what we can see on the screen. We use React DOM to render an element into the DOM of an HTML document.
// Suppose that we have a Div named "root" in the HTMLconst element = <h1>Hello, world</h1>;ReactDOM.render(element, document.getElementById('root'));
React elements are immutable after creation. Thus, the only way to update UI is to create a new element and pass it to the ReactDOM.render(). Only changes are updated to the DOM, not the entire tree.
Components and Props
The purpose of components is spliting the UI into independent, reusable pieces, so that we can think of each piece if isolation.
Conceptually, components are like JS functions. They accept arbitrary inputs (i.e., "props") and return React elements.
Components can be specified as functions:
function Welcome(props) {return <h1>Hello, {props.name}</h1>;}
Alternatively, components can be specified as ES6 classes:
class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;}}
Properties passed to components are immutable. Thus, all React components must act like pure functions with respect to their props.
Sample code:
// Define componentsfunction Welcome(props) {return <h1>Hello, {props.name}</h1>;}function App() {// Notice the use of Welcome as the tagreturn (<div><Welcome name="Sara" /><Welcome name="Cahal" /><Welcome name="Edite" /></div>);}ReactDOM.render(<App />,document.getElementById('root'));
State and Lifecycle
Each React component has an internal state object. This object is private and fully controlled by the component.
Sample code:
class Clock extends React.Component {constructor(props) {super(props); // Must always call super(props)this.state = {date: new Date()}; // the internal state of this component}// This function is called after the component has been rendered to DOMcomponentDidMount() {// Call the tick() function every secondthis.timerID = setInterval(() => this.tick(),1000);}// This function is called when the component would be removed from DOMcomponentWillUnmount() {clearInterval(this.timerID);}// This function update the internal state of the componenttick() {// MUST USE setState()// setState() automatically invokes render()this.setState({date: new Date()});}render() {// Notice the use of this.state.date.toLocaleTimeString()return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}}ReactDOM.render(<Clock />,document.getElementById('root'));
Data Flows Down
Only a component knows and access its internal state. . It an pass this information down to its children as props. This approach is called "top-down" data flow.
Sample code:
// Suppose that we are in the parent component code// Define a new component with a props having attribute "date"function FormattedDate(props) {return <h2>It is {props.date.toLocaleTimeString()}.</h2>;}// Pass the this.state.date as the props of the child component<FormattedDate date={this.state.date} />
Handling Events
Sample code using Class definition of components:
class Toggle extends React.Component {constructor(props) {super(props);this.state = {isToggleOn: true};// This binding is necessary to make `this` work in the callbackthis.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'));
Sample code using Function definition of components:
function ActionLink() {function handleClick(e) {e.preventDefault();console.log('The link was clicked.');}return (<a href="#" onClick={handleClick}>Click me</a>);}
Sample code for passing arguments to the Event handlers:
// e represents the React event and id represent the arguments<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button><button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
Lists and Keys in React
We can build collections of elements, store them in variables, and include them in JSX using curly braces.
Keys help React identify which items that have been changed, added, or removed.
Code sample:
function ListItem(props) {// Correct! There is no need to specify the key here:return <li>{props.value}</li>;}// This component receives a list of numbers from props and create a list elementfunction NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =>// Correct! Key should be specified inside the array.<ListItem key={number.toString()} value={number} />);return (<ul>{listItems}</ul>);}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root'));
Controlled Components and Forms
HTML forms naturally keep some internal state. Therefore, React has to take over the internal state of the HTML elements. It means that the React components rendering form elements also control what happen with those elements on subsequent user input.
class NameForm extends React.Component {constructor(props) {super(props);this.state = {value: ''};this.handleChange = this.handleChange.bind(this);this.handleSubmit = this.handleSubmit.bind(this);}// notice the event.target.valuehandleChange(event) {this.setState({value: event.target.value});}handleSubmit(event) {alert('A name was submitted: ' + this.state.value);event.preventDefault();}render() {// Notice how this React component controls the value of the <input type="text">// Also notice the use of this.handleChangereturn (<form onSubmit={this.handleSubmit}><label>Name:<input type="text" value={this.state.value} onChange={this.handleChange} /></label><input type="submit" value="Submit" /></form>);}}
Handling React form elements can be tedious. It is better to rely on solutions such as Formik: https://jaredpalmer.com/formik
Lifting State Up
When several components need to reflect the same changing data, their shared state should be lifted up to their closest common ancestor.
Procedure of lifting the state:
- Move the state from the child component to the parent component
- Define the handler functions in the parent component to change the state
- The parent component passes that state information to the child components via props
- The parent component passes the handler function to the child components via props
- The child component uses the passed handler to handle events and passed props to render its content.
Code sample:
// The calculator component is the parent component// It aims to keep both input fields, representing celcius and fahrenheit in syncclass Calculator extends React.Component {constructor(props) {super(props);this.handleCelsiusChange = this.handleCelsiusChange.bind(this);this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);// The temperature in both c and f is maintained as the state of the calculator componentthis.state = {temperature: '', scale: 'c'};}// Re-render components when there are changeshandleCelsiusChange(temperature) {this.setState({scale: 'c', temperature});}handleFahrenheitChange(temperature) {this.setState({scale: 'f', temperature});}// During the rendering of the calculator component, new child components (i.e., temperature inputs) are rendered with new props, which come from the state information of the calculatorrender() {const scale = this.state.scale;const temperature = this.state.temperature;const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;return (<div><TemperatureInputscale="c"temperature={celsius}onTemperatureChange={this.handleCelsiusChange} /><TemperatureInputscale="f"temperature={fahrenheit}onTemperatureChange={this.handleFahrenheitChange} /><BoilingVerdictcelsius={parseFloat(celsius)} /></div>);}}// Temperature Input is the child componentclass TemperatureInput extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);}handleChange(e) {// Noted that the function to handle the event is passed by the parent component via propsthis.props.onTemperatureChange(e.target.value);}render() {// Noted that the values representing the state of this component are also passed by its parent via propsconst temperature = this.props.temperature;const scale = this.props.scale;return (<fieldset><legend>Enter temperature in {scaleNames[scale]}:</legend><input value={temperature}onChange={this.handleChange} /></fieldset>);}}
Composition of Components
Containment
Some generic components such as Sidebar or Dialog do not know their children in advance. In this case, they can use props.children to pass children elements directly into their output:
// Containing elementfunction FancyBorder(props) {return (<div className={'FancyBorder FancyBorder-' + props.color}>{props.children}</div>);}// Contained elementfunction WelcomeDialog() {// The <h1> and <p> are passed as props.childrenreturn (<FancyBorder color="blue"><h1 className="Dialog-title">Welcome</h1><p className="Dialog-message">Thank you for visiting our spacecraft!</p></FancyBorder>);}
Specialization
Some components might be special cases of other components. In React, this can be done by having the more specific components render a more "generic" component and configures it with props.
// This is generic componentfunction Dialog(props) {return (// Note that props.title, props.message, and props.children allow specialising the component<FancyBorder color="blue"><h1 className="Dialog-title">{props.title}</h1><p className="Dialog-message">{props.message}</p>{props.children}</FancyBorder>);}// This is specific componentclass SignUpDialog extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);this.handleSignUp = this.handleSignUp.bind(this);this.state = {login: ''};}render() {return (// <input> and <button> will be passed to props.children// title and message are passed to props.title and props.message<Dialog title="Mars Exploration Program"message="How should we refer to you?"><input value={this.state.login}onChange={this.handleChange} /><button onClick={this.handleSignUp}>Sign Me Up!</button></Dialog>);}handleChange(e) {this.setState({login: e.target.value});}handleSignUp() {alert(`Welcome aboard, ${this.state.login}!`);}}
Thinking in React
Steps to design a React app:
- Start with a Mock of the interface and the sample data (e.g., JSON)
- Break the UI into a component hierarchy. Single responsibility principle can be a starting point.
- Build a static version in React. Don't use any state, only props.
- Identify the minimal (but complete) Representation of UI State. Use the DRY principle as the starting point.
- Identify where your state should live.
- Add Inverse Data Flow (i.e., lifting the state information up from the bottom of the hierarchy)
Questions to determine if a piece of data is a state:
- Is is passed from a parent via props? If so, it probably isn't state.
- Does it remain unchanged over time? If so, it probably isn't state.
- Can you compute it based on any other state or props in your component? If so, it isn't state.
How to determine the position of state:
- Identify every component that renders something based on that state
- Find common owner component of those components
- Either the common owner or a component higher up should own the state
- If you cannot find a component, you can create new component solely for holding the state
Hooks
Hooks are new addition to React 16.8. They allow using state and other React features without writing a class.
Hooks are functions that "hook into" React state and lifecycle features from function components. Thus, they don't work inside classes. We can create our own Hooks to reuse stateful behaviour between different components.
TBA
Design Patterns for React
Render Props
TBA
Higher-Order Components
TBA