Skip to content

Latest commit

 

History

History
1132 lines (918 loc) · 29.2 KB

react-summary.md

File metadata and controls

1132 lines (918 loc) · 29.2 KB

React Summary

Resources

How to use React?

With Codepen

To use React, you can either play on Codepen with this Hello World React example on Codepen.

With CodeSanbox

If you want something very quick to use, very close to what you can have with VS Code, you can go on CodeSanbox.io: https://codesandbox.io/s/new

With create-react-app

Or you can create a React application with your terminal:

# To create a new React app
$ npx create-react-app my-app
$ cd my-app

# To start the React app
$ npm start # or yarn start

Then you have the following architecture:

  • node_modules/
  • public/: All the files here are accessible by the user
    • index.html: The HTML page displayed
  • src/: All the React code
    • App.css
    • App.js
    • App.test.js
    • index.css
    • index.js: The main file
    • logo.svg
    • serviceWorker.js
  • .gitignore
  • package.json

VS Code extensions for React

To have many useful shortcuts, you can install ES7 React/Redux/GraphQL/React-Native snippets.

// Shortcut: imp→	
import moduleName from 'module'

// Shortcut: imn→	
import 'module'

// Shortcut: imd→	
import { destructuredModule } from 'module'

// Shortcut: exp→ (→ is TAB)
export default moduleName

// Shortcut: exd→
export { destructuredModule } from 'module'

// Shortcut: rfc→ (React Function Component)
import React from 'react'

export default function Filename() {
  return (
    <div>
      $1
    </div>
  )
}

Import/Export

// variables.js

// There is maximum 1 export default
export default 10

export const a = 1
export let b = 2
export function c () {
  return 3
}
// playground.js

import x, { a, b, c } from "./variables.js";

console.log(x)   // => 10
console.log(a)   // => 1
console.log(b)   // => 2
console.log(c()) // => 3


import { a as apple, b as banana, c as carrot } from "./variables.js";

console.log(apple)    // => 1
console.log(banana)   // => 2
console.log(carrot()) // => 3

The minimum code you need for React

<!-- index.html -->
<html>
<head>
  <title>React App</title>
</head>
<body>
  <!-- The React content will be injected in this div -->
  <div id="root"></div>

  <!-- If you use create-react-app, you don't need to include any JS, it's done autmoatically -->
  <script src="index.js"></script>  
</body>
</html>
// index.js
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
)

Page rendered (Chrome inspector):

<html>
<head>
  <title>React App</title>
</head>
<body>
  <div id="root"><h1>Hello, world!</h1></div>
</body>
</html>

JSX is a syntax extension to JavaScript.

const element = <h1>Hello, world!</h1>

A JSX will be always wrap in 1 tag, opened and closed.

// Syntax error, 2 children
const wrongElement = (
  <section>first section</section>
  <section>second section</section>
)

// No syntax error, everything is wrapped by "<div>"
const rightElement1 = (
  <div>
    <section>first section</section>
    <section>second section</section>
  </div>
)

// No syntax error, everything is wrapped by "<>", that is invisible in the DOM
const rightElement2 = (
  <>
    <section>first section</section>
    <section>second section</section>
  </>
)

// Syntax error, the tag is never closed
const wrongImage = <img src="...">

// No syntax error, the tag is self closing
const rightImage = <img src="..." />

To embedding JavaScript expressions in JSX, you need to use {}

// Simple example with expression
const x = 6
const y = 7
const element1 = <div>Product: {x} * {y} = {x*y}</div>
// renders ====> <div>Product: 6 * 7 = 42</div>

// Simple example with expression
const element2 = <div>Random number: {Math.floor(100 * Math.random())}</div>
// renders ====> <div>Random number: 87</div>

// Simple example with a JavaScript expression in an attribute 
// You mustn't wrap {} by quotes
const picture = "https://media.giphy.com/media/cFdHXXm5GhJsc/giphy.gif"
const element3 = <img src={picture} />
// DON'T DO:     <img src="{picture}" />
// renders ====> <img src="https://media.giphy.com/media/cFdHXXm5GhJsc/giphy.gif" />

const name = 'Smith'
const gender = 'female'
const element4a = <div>Hi {gender === 'male' ? 'Mr' : 'Mrs'} {name}!</div>
const element4b = <div>Hi Mr{gender === 'female' && 's'} {name}!</div>
// both render => <div>Hi Mrs Smith!</div>

const numbers = [1,2,4,8,16]
const element5 = <div>Numbers: {numbers}</div>
// renders ====> <div>Numbers: 124816</div>

const element6 = <ul>{numbers.map((number,i) => <li key={i}>{number}</li>)}</ul>
// renders ====> <ul>
//                 <li>1</li>
//                 <li>2</li>
//                 <li>4</li>
//                 <li>8</li>
//                 <li>16</li>
//               </ul>

A detailed component API reference is available here.

2 differents syntaxes to write a component:

  • Function Component: Can only take props and renders its return value.
  • Class Component: Can use props and state and renders the return value of the render method (old way when there were no hooks).
// Function Component definition
function Welcome(props) {
  return <h1>Hello {props.name}</h1>
}

// Another Function Component definition
function App() {
  return (
    <div>
      {/* Component usage: it's the same for function and class components */}
      <Welcome name="Alice" />
      <Welcome name="Bob" />
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
)
<!-- Content rendered (Chrome inspector) -->
<div id="root">
  <div> <!-- Rendered from <App /> -->
    <h1>Hello Alice</h1> <!-- Rendered from <Welcome1 name="Alice" /> -->
    <h1>Hello Bob</h1>   <!-- Rendered from <Welcome1 name="Bob" /> -->
  </div>
</div>

There is one props special: props.children, explained in the following example:

function MyComponent(props) {
  return <div>{props.children}</div>
}

// In some other components
// ...

{/* It works */}
<MyComponent children="My value" />

{/* Recommanded syntax */}
<MyComponent>My value</MyComponent>

A more complex example is available here.

A component can have values hooked, like in the following example.

import React, { useState } from 'react';

function Example() {
  // Declare a new state hook named `count`, with a setter named `setCount`, initialized to 0
  const [count, setCount] = useState(0);

  return (
    <div>
      {/* The state hook is displayed */}
      <p>You clicked {count} times</p>

      {/* The state hook is changed on click with `setCount` */}
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Try it on CodePen.

These are some rules about hooks:

  • Declare a state hook like this: const [myState, setMyState] = useState(myInitialValue)
  • Never change the state hook directly, always use the setter (setMyState)
  • Only call hooks at the top level, don’t call hooks inside loops, conditions, or nested functions
  • Only call hooks from React functions (components or custom hooks)

You have below a more complex example where the state hook is an object.

function App(props) {
  const [state, setState] = useState({
    random: null,
    history: []
  })

  function handleClick() {
    let r = 1+Math.floor(100*Math.random()) // random value between 1 and 100
    // Set state values
    setState({
      random: r,
      history: [...state.history , r]
    })
  }

  return (
    <div>
      <button onClick={() => handleClick()}>New random value</button> <br/>
      
      {/* Get state values */}
      random: {state.random} <br />
      history: {state.history.map((x,i) => <li key={i}>{x}</li>)} <br />
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
)

Try it on CodePen

React Props VS State

Generic Component

A component takes props as a parameter (from the outside), has a state/hook internally and renders some HTML.

image

Example with a fake Like button

image

React Lifecycle

  • First render of the component (mount)

    • Execute the code of the component and initialize the states hooks => DOM rendered
    • Execute the functions inside useEffect
  • Other renders of the same component (update)

    • Execute the code of the component and reuse the states hooks => DOM rendered
    • Execute the cleanups functions of the useEffect
    • Execute the functions inside useEffect
  • Destruction of the component (unmount)

    • Execute the cleanups functions of the useEffect

Example

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

// index.js
function DisplayInfo(props) {
  console.log("    (1) DisplayInfo");
  useEffect(() => {
    console.log("    (2) DisplayInfo (useEffect)");
    return () => {
      console.log("    (3) DisplayInfo (useEffect cleanup)");
    };
  });
  return (
    <div>
      Hello {props.name}, your name as {props.name.length} characters!
    </div>
  );
}

function App(props) {
  console.log("(1) App");
  const [name, setName] = useState("");
  useEffect(() => {
    console.log("(2) App (useEffect)");
    return () => {
      console.log("(3) App (useEffect cleanup)");
    };
  });
  function handleChange(event) {
    setName(event.target.value);
  }
  return (
    <div>
      {/* When the input is changed, handleChange is triggered and set state.name to the input value */}
      <input
        type="text"
        value={name}
        onChange={event => handleChange(event)}
        placeholder="Type a name"
      />

      {name !== "" && <DisplayInfo name={name} />}
    </div>
  );
}

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

Try it on CodeSandbox and check the console!

Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences:

  • React events are named using camelCase
  • With JSX you pass a function as the event handler

Examples

function alertTheAnswer() {
  alert(42)
}

{/* These 3 buttons are the same and display 42 on click */}

<button onClick={() => alert(42)}>
  What is the answer to life, the universe and everything? 
</button>

<button onClick={() => { alert(42) }}>
  What is the answer to life, the universe and everything? 
</button>

<button onClick={alertTheAnswer}>
  What is the answer to life, the universe and everything? 
</button>
Event Types Event Names Event Properties
Clipboard Events onCopy onCut onPaste clipboardData
Composition Events onCompositionEnd onCompositionStart onCompositionUpdate data
Keyboard Events onKeyDown onKeyPress onKeyUp altKey charCode ctrlKey getModifierState(key) key keyCode locale location metaKey repeat shiftKey which
Focus Events onFocus onBlur relatedTarget
Form Events onChange onInput onInvalid onSubmit
Mouse Events onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp altKey button buttons clientX clientY ctrlKey getModifierState(key) metaKey pageX pageY relatedTarget screenX screenY shiftKey
Selection Events onSelect
Touch Events onTouchCancel onTouchEnd onTouchMove onTouchStart altKey changedTouches ctrlKey getModifierState(key) metaKey shiftKey targetTouches touches
UI Events onScroll detail view
Wheel Events onWheel deltaMode deltaX deltaY deltaZ
Media Events onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
Image Events onLoad onError
Animation Events onAnimationStart onAnimationEnd onAnimationIteration animationName pseudoElement elapsedTime
Transition Events onTransitionEnd propertyName pseudoElement elapsedTime
Other Events onToggle
// Component that display a login or logout button based on props.isLoggedIn
function MyComponent(props) {
  function showButton() {
    if (props.isLoggedIn)
      return <LogoutButton />
    else 
      return <LoginButton />
  }

  let button
  if (props.isLoggedIn)
    button = <LogoutButton />
  else 
    button = <LoginButton />

  return (
    <div>
      {/********** Method 1: Variable **********/}
      {button}
      {/********** Method 2: Function **********/}
      {showButton()}
      {/********** Method 3: Ternary **********/}
      {props.isLoggedIn ? <LogoutButton /> : <LoginButton />}
      {/********** Method 4: Inline If with Logical && Operator **********/}
      {props.isLoggedIn && <LogoutButton />}        
      {!props.isLoggedIn && <LoginButton />}        
    </div>
  )
}
const students = ['Alice', 'Bob', 'Charly', 'David']

// Component that display a list of students
function MyComponent (props) {
  function showList() {
    let list = []
    for (let i = 0; i < students.length; i++) {
      list.push(<li key={i}>{students[i]}</li>)
    }
    return list
  }

  let list = []
  for (let i = 0; i < students.length; i++) {
    list.push(<li key={i}>{students[i]}</li>)
  }

  return (
    <ul>
      {/********** Method 1: Variable **********/}
      {list}
      {/********** Method 2: Function **********/}
      {showList()}
      {/********** Method 3: Map **********/}
      {students.map((student,i) => <li key={i}>{student}</li>)}
    </ul>
  )
}

When we use forms in React, we generally follow this methodology.

First, for each items on the form (<input />, <textarea /> and <select></select>), we set a property value(generally with a state) and a property onChange.

Example:

// Init with 3 state values
const [state, setState] = useState({
  firstName: "",
  occupation: "",
  debt: false,
})
// Component method
function handleChange = (event) => {
  setState({
    ...state,
    [event.target.name]: event.target.value
  });
}
// Some code in the render

{/* This input display "state.firstname" and update this value on change  */}
<input type="text" name="firstname" value={state.firstname} onChange={handleChange} /> 

{/* This textarea display "state.occupation" and update this value on change  */}
<textarea name="occupation" value={state.occupation} onChange={handleChange} /> 

{/* This select display "state.debt" and update this value on change  */}
<select name="debt" value={state.debt} onChange={handleChange}>
  <option value={true}>Yes</option>
  <option value={false}>No</option>
</select>

Then, we need to listen at the form submission, with onSubmit.

Example:

function handleSubmit = (event) => {
  event.preventDefault() // To avoid going to the action page of the form

  // The code to be executed when the user submit the form (click on a button)
}
// Some code in the render

<form onSubmit={handleSubmit}>
  {/* ... */}
</form>

Full example of a component that display a list of characters and let the user add some. Be careful, it's a shared API ;)

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import axios from "axios";

function App(props) {
  const [state, setState] = useState({
    characters: [],
    name: "", // Default value for the form
    occupation: "", // Default value for the form
    debt: false, // Default value for the form
    weapon: "" // Default value for the form
  });
  function handleChange(event) {
    setState({
      ...state,
      [event.target.name]: event.target.value // the value of the input
    });
  }
  function handleSubmit(event) {
    event.preventDefault();
    const character = {
      name: state.name,
      occupation: state.occupation,
      debt: state.debt,
      weapon: state.weapon
    };
    axios
      .post("https://ih-crud-api.herokuapp.com/characters", character)
      .then(res => {
        console.log(res);
        console.log(res.data);
      });
  }
  function handleClick() {
    setState({ ...state, characters: [] });
    axios.get("https://ih-crud-api.herokuapp.com/characters").then(response => {
      setState({
        ...state,
        characters: response.data
      });
    });
  }
  return (
    <div>
      <h2>Add a character</h2>
      <form onSubmit={handleSubmit}>
        Name:
        <input
          type="text"
          name="name"
          value={state.name}
          onChange={handleChange}
        />
        <br />
        Occupation:
        <textarea
          name="occupation"
          value={state.occupation}
          onChange={handleChange}
        />
        <br />
        Debt:
        <select name="debt" value={state.debt} onChange={handleChange}>
          <option value={true}>Yes</option>
          <option value={false}>No</option>
        </select>
        <br />
        Weapon:
        <input
          type="text"
          name="weapon"
          value={state.weapon}
          onChange={handleChange}
        />
        <br />
        <button type="submit">Add</button>
      </form>

      <h2>Display all characters</h2>
      <button onClick={handleClick}>Display the characters from the API</button>

      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Occupation</th>
            <th>Debt</th>
            <th>Weapon</th>
          </tr>
        </thead>
        <tbody>
          {state.characters.map(c => (
            <tr key={c.id}>
              <td>{c.name}</td>
              <td>{c.occupation}</td>
              <td>{c.debt ? "Yes" : "No"}</td>
              <td>{c.weapon}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

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

Try it on CodeSandbox

Installation

$ npm install react-router-dom

Import

import { BrowserRouter, Route, Switch, Link, NavLink } from 'react-router-dom'

React Router Components

Component Description Main Props
<BrowserRouter> Router Component that should wrap your application
<Route> When the url matches its props path, it renders its content
  • path: string
  • component: Component
  • render: func
  • exact: bool
<Switch> Group <Route> together and display maximum 1
<Link> Replace the <a> tag of HTML in React Router
  • to: string
  • to: object
<NavLink> A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL
  • activeClassName: string
  • activeStyle: object
  • exact: bool
<Redirect> Will navigate to a new location
  • to: string

match

A component displayed with <Route> has access to match (as props.match or as ({ match }) => ()) and it is an object containing the following properties:

Property Type Description
params object Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
isExact bool true if the entire URL was matched (no trailing characters)
path string The path pattern used to match. Useful for building nested <Route>s
url string The matched portion of the URL. Useful for building nested <Link>s

The Minimum Website with react-router-dom

First, we have to enable the routing in the React application with <BrowserRouter>

// src/index.js

// Many imports...
// We import "BrowserRouter" and name it "Router"
import { BrowserRouter as Router } from 'react-router-dom';

// We need to wrap <App /> by <Router> to use routing inside it
ReactDOM.render((
  <Router>
    <App />
  </Router>
), document.getElementById('root'));

Second, we have to set some routes, with <Route /> (and <Switch>), generally in App.js (and sometimes in extra files).

// src/components/App.js
import React, { Component } from 'react';
import Home from './components/Home';
import About from './components/About';
import MyNavbar from './components/Navbar';
import { Switch, Route } from 'react-router-dom';

function App(props) {
  return (
    <div className="App">
      <MyNavbar />

      {/* With <Switch>, maximum 1 route is executed */}
      {/* The Home component will be displayed if the URL is exactly "/" */}
      {/* The About component will be displayed if the URL starts with "/about" */}
      {/* "404" will be rendered in any case (if the previous routes failed) */}
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/about' component={About}/>
        <Route render={() => <h1>404</h1>}/>
      </Switch>
    </div>
  );
}

export default App;

Third, everytime we want to go to another page/URL, we never use <a> but <Link> or <NavLink>

// src/components/Navbar.js
import React from 'react';
import { NavLink } from 'react-router-dom';

export default function Navbar(props) {
  return (
    <nav>
      <ul>
        {/* Add the class "bold" if the URL match the path (if nothing is precised, the default would be "active") */}
        <li><NavLink exact to="/" activeClassName="bold">Home</NavLink></li>
        <li><NavLink to="/about" activeClassName="bold">About</NavLink></li>
      </ul>
    </nav>
  )
}
/* src/index.css */
.bold {
  font-weight: bold;
}

How to create a route with some variable in the URL

Let's say we want to be able to extract the end of the following URL:

First, we have to create a <Route> with some pattern. Example:

// Any component file, for example: src/components/App.js
<Route path="/country/:countryName" component={CountryDetail}/>

Then, we can create the component. Example:

// src/components/CountryDetail.js
import React, { Component } from 'react'

export default function CountryDetail(props) {
  return (
    <div>
      {/* We can access the value with: props.match.params.countryName */}
      Country: {props.match.params.countryName}
    </div>
  )
}

Finally, we can have some link to this route. Example:

// Any component file

// In render
let countries = ['France','Germany','Spain','Netherlands']
return (
  <div>
    {countries.map(c => <Link key={c} to={"/country/"+c}>{c}</Link>)}
  </div>
)

Using Axios with React

Installation

$ npm install axios
import axios from 'axios'

Example of GET request

// Display all users from the API
function PersonList(props) {
  const [persons, setPersons] = useState(null) // init to null
  useEffect(() => {
    axios.get(`https://jsonplaceholder.typicode.com/users`)
      .then(res => {
        const persons = res.data;
        setState({ persons });
      })
  })

  // If state.persons is null, it means we don't have the data from the API yet
  if (!persons) {
    return <div>Loading...</div>
  }

  return (
    <ul>
      { persons.map(person => <li>{person.name}</li>) }
    </ul>
  )
}

Example of POST request

// Add a person thanks to the API
function AddPerson(props) {
  const [state, setState] = useState({ name: '' })

  funciton handleChange(event) {
    setState({ 
      ...state,
      [event.target.name]: event.target.value 
    });
  }

  function handleSubmit(event) => {
    event.preventDefault();
    const user = {
      name: state.name
    };
    axios.post(`https://jsonplaceholder.typicode.com/users`, user)
      .then(res => {
        console.log(res.data)
        // Redirect to the Home page
        props.history.push('/')
      })
  }

  render() {
    return (
      <div>
        <form onSubmit={handleSubmit}>
          Person Name:
          <input type="text" name="name" onChange={handleChange} />
          <button type="submit">Add</button>
        </form>
      </div>
    )
  }
}

Base instance

// src/api.js (in the client folder)
import axios from 'axios';

export default axios.create({
  baseURL: `http://jsonplaceholder.typicode.com/`
  withCredentials: true
})

const errHandler = err => {
  console.error(err)
  if (err.response && err.response.data) {
    console.error("API response", err.response.data)
  }
  throw err
}

export default {
  service: service,

  // Promise to return all users
  getUsers() {
    return service
      .get('/users')
      .then(res => res.data)
      .catch(errHandler)
  },

  // Promise to return 1 user
  getUser(id) {
    return service
      .get('/users/'+id)
      .then(res => res.data)
      .catch(errHandler)
  },

  // Promise to create a user
  addUser(user) {
    return service
      .post('/users', user)
      .then(res => res.data)
      .catch(errHandler)
  },
}
// src/index.js
import api from './api';

// ...
api.getUser(1)
  .then(user => {
    console.log(user);
  })
// ...

Some hints when you use an API in a React website:

  • If you want to display some data from an API, generally

Code Examples

Extra done in class

  • Inline styling
  • Reactstrap (React + Bootstrap)
  • Mapbox and React
  • React and SCSS