React

Submitted by sylvia.wong@up… on Mon, 01/31/2022 - 17:38

You can choose to work through EITHER the React topic, OR the Vue topic for this module. You do NOT need to cover both topics, although you can choose to learn both frameworks if you wish. The mōhio (formative assessment) is the same in both topics, and you should only complete it once. The framework you select will impact group work for the summative assessment.

In this topic, we will cover:

  • Getting started
  • Task automation – Create React App
  • Main concepts
  • Hooks
  • Building a CRUD interface
  • App router.
Sub Topics

Created and maintained by Meta (formerly known as Facebook), React is a JavaScript library for building user interfaces. It is often referred to as a frontend “framework” because it is directly comparable to frameworks like Angular or Vue.22 React is:

  • Declarative – React is the view layer of a model-view-controller (MVC) application.23 It efficiently renders just the right components when your data changes. This makes your code predictable and easier to debug.
  • Component-based – React allows you to build encapsulated components that manage their own state and can be composed to make complex UIs. As component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.
  • Flexible – React lets you develop new features without re-writing existing code, meaning you can adopt just what you need for your technology stack.

React’s features include:

  • A simple component – React components implement a render() method that takes input data and returns what to display.
  • A stateful component – In addition to taking input data, a component can maintain internal state data. When a component’s state data changes, the rendered markup will be updated by re-invoking render().
  • An application – We can use props and state to create small applications such as a Todo list.
  • A component using external plugins – React allows you to interface with other libraries and frameworks.24

Create React App is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration.43 Its tagline is “Set up a modern web app by running one command.” The benefits include:

  • Less to learn – You do not need to learn and configure many build tools. Instant reloads help you focus on development. When it is time to deploy, your bundles are optimised automatically.
  • Only one dependency – Your app only needs one build dependency. Create React App is tested to ensure all its underlying pieces work together seamlessly.
  • No lock-in – Under the hood, webpack, Babel, ESLint, and other amazing projects are used to power your app. If you ever want an advanced configuration, you can “eject” from Create React App and edit their config files directly.
  • Get started in seconds – Whether you are using React or another library, Create React App lets you focus on code, not build tools.
  • Easy to maintain – When new versions of Create React App are released, you can upgrade using a single command: npm install react-scripts@latest.44

Watch this video for a quick overview of Create React App.

Create React App:

  • sets up your development environment so you can use the latest JavaScript features
  • provides an enjoyable developer experience
  • optimises your app for production.

You will need to have Node >= 14.0.0 and npm >= 5.6 on your machine.

Create React App does not handle backend logic or databases; it just creates a frontend build pipeline, so you can use it with any backend you want.

Creating an app

There are a few methods to create a new app. Choose one of the following commands to run in a new terminal on VS Code:

  • npx

    npx create-react-app my-app

    Note: npx comes with npm 5.2+ and higher.

  • npm

    npm init react-app my-app

    Note: npm init <initializer> is available in npm 6+.

  • Yarn

    yarn create react-app my-app

    Note: yarn create is available in Yarn 0.25+.

Selecting a template

You can now optionally start a new app from a template by appending --template [template-name] to the creation command. If you do not select a template, React creates your project with their base template.

Templates are always named in the format cra-template-[template-name], however you only need to provide the [template-name] to the creation command.

npx create-react-app my-app --template [template-name]

You can find a list of available templates by searching for "cra-template-*" on npm.

Selecting a package manager

Developers can use package management tools to automate finding, downloading, installing, configuring, upgrading, and removing a system's packages.

When you create a new app, the CLI will use npm or Yarn to install dependencies, depending on which tool you use to run create-react-app. For example:

# Run this to use npm
npx create-react-app my-app

# Or run this to use yarn
yarn create react-app my-app

Output

Running any of these commands will create a directory called my-app inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies:

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── serviceWorker.js
    └── setupTests.js

There is no configuration or complicated folder structures, only the files you need to build your app. Once the installation is done, you can open your project folder: cd my-app.

Scripts

Inside the newly created project, you can run some built-in commands:

  • npm start or yarn start – runs the app in development mode. Open localhost:3000 to view it in the browser. The page will automatically reload if you make changes to the code. You will see the build errors and lint warnings in the console.
  • npm test or yarn test – runs the test watcher in an interactive mode. By default, it runs tests related to files changed since the last commit.
  • npm run build or yarn build – builds the app for production to the build folder. It correctly bundles React in production mode and optimises the build for the best performance.43

Now let us cover the main concepts in React. More information about each concept can be found on the React website.

JSX

JSX is a syntax extension to JavaScript. We use it to create React “elements.” While it is not required to use JSX with React, it is recommended to describe what the UI should look like. JSX provides a helpful visual aid when working with UI inside the JavaScript code. It also allows React to show more useful error and warning messages.

Embedding expressions in JSX

We declare a variable and then use it inside JSX by wrapping it in curly braces. You can put any valid JavaScript expression inside the curly braces in JSX.

In the example following, we embed the result of calling a JavaScript function, formatName(user), into an <h1> element:

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")
);

JSX should be formatted over several lines for readability, and to avoid syntax errors.

JSX expressions

After compilation, JSX expressions become regular JavaScript function calls and evaluate to JavaScript objects. This means you can use JSX inside if statements and for loops, assign it to variables, accept it as arguments, and return it from functions:

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }

  return <h1>Hello, Stranger.</h1>;
}

Specifying attributes with JSX

You can use quotes to specify string literals as attributes:

const element = <a href="https://www.reactjs.org">link</a>;

You can also use curly braces to embed a JavaScript expression in an attribute:

const element = <img src={user.avatarUrl} />;

Do not put quotes around curly braces when embedding a JavaScript expression in an attribute – use either quotes (for string values) or curly braces (for expressions), but not both in the same attribute. As JSX is closer to JavaScript than HTML, React DOM uses camelCase property naming convention instead of HTML attribute names.

Specifying children with JSX

If a tag is empty, you can close it immediately with />, like XML. JSX tags may also contain children:25

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

Preventing injection attacks with JSX

DOM-based cross-site scripting is one of the most common types of injection attacks. When you mutate DOM directly, it becomes easy for an attacker to inject it with data containing malicious JavaScript.26 By default, React DOM escapes any values embedded in JSX before rendering them. This ensures that you can never inject anything that is not explicitly written in your application. Everything is converted to a string before being rendered.

JSX represents objects

React.createElement() performs a few checks to help you write bug-free code, but essentially it creates an object like this:

// Note: this structure is simplified
const element = {
  type: "h1",
  props: {
    className: "greeting",
    children: "Hello, world!",
  },
};

These objects are called “React elements.” Think of them as descriptions of what you want to see on the screen. React reads these objects and uses them to construct and update the DOM.25

Rendering elements

Elements are the smallest building blocks of React apps. Unlike browser DOM elements, React elements are plain objects, and React (virtual) DOM updates the DOM to match the React elements.

Everything inside a <div> in your HTML file is managed by a "root" DOM node:

<div id="root"></div>

Applications built entirely in React usually have a single root DOM node. If you are integrating React into an existing app, you can have as many isolated root DOM nodes as you like.

React elements are immutable. Once you create an element, you cannot change its children or attributes. The only way to update the UI is to create a new element and pass it to ReactDOM.render().

React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state.27

A diagram depicting React's Virtual DOM
Adapted from Performing calculations in the Virtual DOM limits rerendering … © O’Reilly Media, Inc.

Components and props

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen. JavaScript functions in React are called function components.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

You can also use an ES6 class to define a component. These are called class components.

class Welcome extends React.component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Elements can also represent user-defined components. When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object. We call this object “props.”

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;

ReactDOM.render(
  element,
  document.getElementById("root")
);

Whether you declare a component as a function or a class, it must never modify its own props.

Functions that do not attempt to change their inputs and always return the same result for the same inputs are “pure.” Functions that change their own input are “impure.”

React is flexible but has one strict rule: All React components must act like pure functions with respect to their props.28

State and lifecycle

Components can have “state,” which is an object that determines how a component renders and behaves. State is like props, but private and fully controlled by the component.29

In previous versions of React, only class components could use state. However, the addition of hooks in React 16.8 allows you to use state without writing class.30 We will learn more about hooks later.

The creation phase of a React component’s lifecycle is called mounting, while the deletion phase is called unmounting.31 In applications with many components, it is important to free up resources taken by the components when they are destroyed. Lifecycle methods are special methods we hook into when a function component mounts and unmounts.29

A diagram depicting React's Lifecycle Methods
Adapted from A Deep Dive into React Lifecycle Methods by Viduni Wickramarachchi, © Viduni Wickramarachchi

setState()

setState() queues changes to the component state, and tells React that this component and its children need to be re-rendered with the updated state. We can think of setState() as a request rather than an immediate command to update the component. This is the primary method of updating the user interface in response to event handlers and server responses.

  • State cannot be modified directly – Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
  • State updates may be asynchronous – React may batch multiple setState() calls into a single update for performance.
  • State updates are merged – When you call setState(), React merges the object you provide into the current state.

Parent and child components will not know if a certain component is stateful or stateless, and it should not matter whether the component is defined as a function or a class. As such, state is often called “local” or “encapsulated”. The data flows down – it is not accessible to any component other than the one that owns and sets it. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree.

State is an implementation detail of the component that may change over time. You can use stateless components inside stateful components, and vice versa.29

Use a stateless component if: Use a stateful component if:
  • you just need to present props
  • you do not need a state or any internal variables
  • you are creating a non-interactive element
  • you want to reuse the code.
  • you are building an element that accepts user input, or is interactive
  • you are dependent on state for rendering (for example, data fetching)
  • you are dependent on data that cannot be passed down as props.

Stateful vs. Stateless React Components32

Handling events

Handling events with React elements is similar to handling events on DOM elements. However, there are some syntax differences:

  • React events are named using camelCase rather than lowercase.
  • With JSX, you pass a function as the event handler rather than a string.

For example, the HTML is slightly different in React:

<button onClick={activateLasers}>
  Activate Lasers
</button>

Another difference is that you cannot return false to prevent default behaviour in React. You must call preventDefault explicitly:

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log("You clicked submit.");
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</submit>
    </form>
  );
}

When using React, you generally do not need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.

When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. For example, this 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(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? "ON" : "OFF"}
      </button>
    );
  }
}

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

Be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is called.33

Conditional rendering

In React, you can create distinct components that encapsulate behaviour you need. Then you can render necessary components depending on the state of your application.

Conditional rendering in React works the same way conditions work in JavaScript. Use JavaScript operators like if or the conditional operator to create elements representing the current state, and let React update the UI to match.

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;

  if (isLoggedIn) {
    return <UserGreeting />;
  }

  return <GuestGreeting />;
}

ReactDOM.render(
  // Try changing to isLoggedIn={true}
  <Greeting isLoggedIn={false}>,
  document.getElementById("root")
);

This example renders a different greeting depending on the value of isLoggedIn prop.34

Lists and keys

In React, transforming arrays into lists of elements is nearly identical to transforming lists in JavaScript.

You can build collections of elements and include them in JSX using curly braces {}.

In the example that follows, we loop through the numbers array using the JavaScript map() function, return a <li> element for each item, and assign the resulting array of elements to listItems:

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li>{number}</li>);

Then, we can include the entire listItems array inside a <ul> element:

<ul>{listItems}</ul>

Usually, you would render lists inside a component.

A “key” is a special string attribute you need to include when creating lists of elements. They help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity, and only make sense in the context of the surrounding array.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) => (
    <li key={number.toString()}>
      {number}
    </li>
  ));

  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];

ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById("root")
);

Keys used within arrays should be unique among their siblings. However, they do not need to be globally unique.35

Forms

In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().

We can combine the two by making the React state the “single source of truth.” The React component that renders a form then also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component.”

class NameForm extends React.component {
  constructor(props) {
    super(props);
    this.state = {
      value: "",
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }
  handleSubmit(event) {
    alert("A name was submitted: " + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
          </label>
          <input type="submit" value="Submit" />
      </form>
    );
  }
}

Because the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth. Since handleChange() runs on every keystroke to update the React state, the displayed value will update as the user types.

With a controlled component, the input’s value is always driven by the React state. While this means you need to write more code, it allows you to pass the value to other UI elements, or reset it from other event handlers.36

Lifting state up

Often, several components need to reflect the same changing data. In React, state is shared by moving it up to the closest common ancestor of the components that need it. This is called “lifting state up.”

There should be a single “source of truth” for any data that changes in a React application. Usually, the state is first added to the component that needs it for rendering. Then, if other components also need it, you can lift it up to their closest common ancestor. Instead of trying to sync the state between different components, you should rely on the top-down data flow.37

A diagram depicting 'Lifting State Up' in React
Adapted from What is Called “Lifting State Up” In React by Bhargav Bachina, © Bhargav Bachina

Thinking in React

React encourages you to think about apps as you build them. Here are the steps for building a searchable data project table using React.

Start with a mock.

1. Break the UI into a component hierarchy

A diagram depicting a breakdown of React Components
Adapted from Thinking in React, © Meta Platforms, Inc.

This example identifies five app components and arranges them into a hierarchy:

  1. FilterableProductTable – contains the entirety of the example
  2. SearchBar – receives all user input
  3. ProductTable – displays and filters the data collection based on user input
  4. ProductCategoryRow – displays a heading for each category
  5. ProductRow – displays a row for each product.

2. Build a static version in React

To build a static version of your app that renders your data model, you will want to build components that reuse other components and pass data using props. Do not use state at all to build this static version. State is reserved only for interactivity, which you do not need in a static version of an app.

3. Identify the minimal (but complete) representation of UI state

To make your UI interactive, you need to be able to trigger changes to your underlying data mode with state.

To build your app correctly, first think of the minimal set of mutable state your app needs – the key here is DRY: Don’t Repeat Yourself. Once you have done that, compute everything else you need on-demand.

In the example, our state is:

  • The search text the user has entered.
  • The value of the checkbox.

4. Identify where your data should live

Once you have identified what the minimal set of app state is, identify which component mutates or owns this state.

  • ProductTable needs to filter the product list based on state, and SearchBar needs to display the search text and checked state.
  • The common owner component is FilterableProductTable.
  • It conceptually makes sense for the filter text and checked value to live in FilterableProductTable.

5. Add inverse data flow

Finally, we need to support data flowing the other way: the form components deep in the hierarchy need to update the state in FilterableProductTable.

We want to make sure that whenever the user changes the form, we update the state to reflect the user input. As components should only update their own state, FilterableProductTable will pass callbacks to SearchBar that fire whenever the state should be updated. We can use the onChange event on the inputs to be notified. The callbacks passed by FilterableProductTable will call setState() to update the app.38

Hooks are functions that let you “hook into” React state and lifecycle features from function components without writing class. Hooks are JavaScript functions with two additional rules:

  • Only call hooks at the top level, before any early returns. Do not call hooks inside loops, conditions, or nested functions. This ensures hooks are called in the same order each time a component renders, which allows React to correctly preserve the state of hooks between multiple calls.
  • Only call hooks from React function components. Do not call hooks from regular JavaScript functions. This ensures that all stateful logic in a component is clearly visible from its source code.

A linter plugin is available to enforce these rules.

React provides a set of built-in hooks, and you can also create your own custom hooks – custom hooks are the only other valid place to call hooks.39 Now let us explore at a few basic built-in hooks.

useState

The state hook returns a stateful value, and a function to update it.40

import React, { useState } from "react";

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);
}

During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).

The setState() function is used to update the state. It accepts a new state value and enqueues a re-render of the component.

The only argument to the useState() hook is the initial state. Unlike with classes, the state does not have to be an object. We can keep a number or a string if that is all we need. In the example previous, we just want a number for how many times the user clicked, so pass 0 as initial state for our variable. If we wanted to store two different values in state, we would call useState() twice.41

useEffect

The effect hook lets you perform side effects in function components. Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. There are two common kinds of side effects in React components: those that do not require cleanup, and those that do.

Effects without cleanup

Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that do not require a cleanup, because we can run them and immediately forget about them.

useEffect() tells React your component needs to do something after render. React will remember the function you passed (“effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.

import React, { useEffect, useState } from "react";

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Calling useEffect() inside the component lets us access the count state variable (or any props) right from the effect. We do not need a special API to read it — it is already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution.

By default, the hook runs both after the first render and after every update. Instead of thinking in terms of “mounting” and “updating,” you might find it easier to think that effects happen “after render.” React guarantees the DOM has been updated by the time it runs the effects.

Effects with cleanup

Some effects do require cleanup. For example, we might want to set up a subscription to an external data source. In that case, it is important to clean up so we do not introduce a memory leak.

For example, say we have a ChatAPI module that lets us subscribe to a friend’s online status. Instead of needing a separate effect to perform the cleanup, code for adding and removing a subscription is so tightly related that useEffect() is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:

import React, { useEffect, useState } from "react";

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    // Specify how to clean up after this effect
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return "Loading...";
  }

  return isOnline ? "Online" : "Offline";
}

Every effect may return a function that cleans up after it. React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render. Therefore, React also cleans up effects from the previous render before running the effects next time.42

useContext

const value = useContext(MyContext);

useContext accepts a context object (the value returned from React.createContext), and returns the current context value for that context. The current context value is determined by the value prop of the nearest <MyContext.Provider> above the calling component in the tree.

When the nearest <MyContext.Provider> above the component updates, this hook will trigger a re-render with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a re-render will still happen starting at the component itself using useContext().

The argument to useContext() must be the context object itself: useContext(MyContext).

A component calling useContext() will always re-render when the context value changes. If re-rendering the component is expensive, you can optimise it by using memoization.39

CRUD is an acronym for the four basic functions required to implement a persistent storage application: create, read, update, and delete. If an organisation needs to retain customer information (for example, personal details or payment information), they will require an application that provides persistent storage, usually in the form of an electronic database. Users can then perform CRUD operations on data within the database.

  • Create – The create function allows users to make a new record in the database, and populate it with data that corresponds to the record’s attributes. For example, a book record in a library database might have the attributes “title”, “author”, and "ISBN".50 The database administrator may also be able to add new attributes to the database.
  • Read – The read function allows users to search and retrieve records in the database, and see the value of attributes. Users may also be able to filter the records using keywords or customised criteria. Think of this as the search function in a library catalogue.
  • Update – The update function allows users to modify existing records in a database, for example, changing the values of the attribute fields. Keeping with the library example, a record may need to be updated if the title of a book has a typo.
  • Delete – The delete function allows users to remove records from the database. This would be equivalent to removing a book from the library catalogue. Applications may have a hard delete function which permanently removes the record from a database, or a soft delete, which remove the record from the catalogue, but retains its data.51

MERN

MERN is a popular JavaScript software stack for building CRUD applications, made up of the following components:

  • MongoDB
  • Express.js
  • React.js
  • Node.js.

The MERN architecture allows you to easily construct a 3-tier stack (frontend, backend, database) entirely using JavaScript and JSON.

A diagram depicting a MERN Stack
Adapted from A MERN Stack Explained, © MongoDB, Inc.

React.js frontend

The top tier of the MERN stack is React.js, the declarative JavaScript framework for creating dynamic client-side applications in HTML. React lets you build up complex interfaces through simple components, connect them to data on your backend server, and render them as HTML.

React’s strong suit is handling stateful, data-driven interfaces with minimal code and pain. It has all the features you would expect from a modern web framework: great support for forms, error handling, events, lists, and more.

Express.js and Node.js server tier

The next level down is the Express.js server-side framework, running inside a Node.js server. Express.js bills itself as a “fast, unopinionated, minimalist web framework for Node.js,” and is just that. It has powerful models for URL routing (matching an incoming URL with a server function), and handling HTTP requests and responses.

You can connect to Express.js functions that power your application by making XML HTTP Requests (XHRs) or GETs or POSTs from your React.js frontend. Those functions in turn use MongoDB’s Node.js drivers, either via callbacks or using promises, to access and update data in your MongoDB database.

MongoDB database tier

If your application stores any data (for example, user profiles, content, comments, uploads, events), then you are going to want a database like MongoDB that is as easy to work with as React, Express, and Node.

JSON documents created in your React.js frontend can be sent to the Express.js server, where they are processed and (assuming they are valid) stored directly in MongoDB for later retrieval. If you are building in the cloud, you will want to look at Atlas.52

Connecting React to API

What you need:

In your React project

Install Axios (like Postman for React):

npm i axios -s

Import axios to your React project

import axios from "axios"

Add methods to access API

var urlPrefix = "http://localhost:4000/api";

CRUD methods in React

getProjects = () => {
  axios.get(urlPrefix + "/projects")
    .then(res => {
      this.setState({ projects: res.data });
    });
}

addProject = (data) => {
  axios.post(urlPrefix + "/projects", data)
    .then(res => {
      this.getProjects();
    });
}

deleteProject = (id) => {
  axios.delete(urlPrefix + "/projects/" + id)
    .then(res => {
      this.getProjects();
    });
}

updateProject = (id, data) => {
  axios.put(urlPrefix + "/projects/" + id, data)
    .then(res => {
      this.getProjects();
    });
}

componentDidMount() {
  this.getProjects();
}

To add a new project, send addProject to the AddProjectForm:

<AddProjectForm addProject={this.addProject} setActiveView={this.setActiveView} />

In AddProjectForm.js:

  handleFormSubmit = (e) => {
    e.preventDefault();

    var formData = new FormData(this.addForm);
    var data = {
      name: formData.get("name-input"),
      description: formData.get("description-input"),
      type_id: parseInt(formData.get("type-input")),
    };
  }

  var { addProject, setActiveView } = this.props;

  addProject(data);
  setActiveView("projects");

  // this.props.addProject(data);
  // this.props.setActiveView("projects");
}

<form onSubmit="{this.handleFormSubmit}" ref="{el}">{this.addForm = el}</form>

To delete
In App.js:

{
  this.state.projects.map((project) => {
    var projectProps = {
      ...project,
      setActiveView: this.setActiveView,
      deleteProject: this.deleteProject,
    };

    return (
      <Project {...projectProps} />
    );
  });
}

In Project.js:

<i onClick={this.handleTrashClick} className="fas fa-trash"></i>;

handleTrashClick = () => {
  var { deleteProject, id } = this.props;
  deleteProject(id);
}

To update
In App state:

projectToUpdate: {
  id: 2,
  name: "The thinking man",
  description: "Bronze sculpture fitted for morden office space",
}

<UpdateProjectForm {...this.state.projectToUpdate} />

In UpdateProjectForm.js:

render() {
  var { name, description } = this.props;

  return (
    <form>
      <div className="form-group">
        <label htmlFor="name-input">Name</label>
        <input type="text" className="form-control" name="name-input" id="name-input" defaultValue={name} />
      </div>
      <div className="form-group">
        <label htmlFor="name-input">Description</label>
        <input type="text" className="form-control" name="description-input" id="description-input" defaultValue={description} />
      </div>
      <div className="form-group">
        <label htmlFor="name-input">Photo</label>
        <input type="text" className="form-control" name="photo-input" id="photo-input" value="project.jpg" />
      </div>
      <div className="form-group">
        <label htmlFor="type-input">Type</label>
        <select className="form-control" name="type-input" id="type-input">
          <option value="1">Painting</option>
          <option value="2">Sculpture</option>
          <option value="3">Digital</option>
        </select>
      </div>
      <button type="submit" className="btn btn-primary">Update</button>
    </form>
  );
}

In app.js:

setProjectToUpdate = (id) => {
  var foundProject = this.state.projects.find((project) => {
    return project.id === id
  })

  this.setState({ projectToUpdate: foundProject })
}

var projectProps = {
  ...project,
  setActiveView: this.setActiveView,
  deleteProject: this.deleteProject,
  setProjectToUpdate: this.setProjectToUpdate,
}

In Project.js:

handleUpdateClick = () => {
  var { id, setActiveView, setProjectToUpdate } = this.props;
  setProjectToUpdate(id);
  setActiveView("update-project");
}

In App.js:

<UpdateProjectForm
  {...this.state.projectToUpdate}
  setActiveView={this.setActiveView}
  updateProject={this.updateProject}
/>

In your UpdateProjectForm.js:

<form onSubmit={this.handleFormSubmit} ref={(el) => {this.updateForm = el}}>

handleFormSubmit = (e) => {
  e.preventDefault();

  var formData = new FormData(this.updateForm);
  var data = {
    name: formData.get("name-input"),
    description: formData.get("description-input"),
  };
  var { id, setActiveView, updateProject } = this.props;

  updateProject(id, data);
  setActiveView("projects");
}

Routes tell the app to only render a certain component if the current URL matches the given path. For routes to work properly in React, you need to wrap your whole application in a router.53

React Router is the most popular Create React App routing solution. To add it, run:

npm install --save react-router-dom

Alternatively you can use yarn:

yarn add react-router-dom

To try it, delete all the code in src/App.js and replace it with any of the examples on the React Router website. The Basic Example is a good place to get started.54

A senior developer reviewing a peer's code

Knowledge check

Frontend client app

This formative assessment is the same in the Vue topic. You only need to work through one topic and complete the activity once.

Objectives

For this project, you should be able to:

  • Use an external API to produce a web interface
  • Utilise a JavaScript framework or user interface library to produce an industry standard single-page application
  • Optimise a client-side application for production

Overview

Utilise a JavaScript framework or user interface library to create a single-page application that allows the user to browse items retrieved from an external API.

The app will be a news app that allows users to view news stories from around the world. It will use the News API.

The basic use cases for the application are:

  • View Article List
  • View About Page (about the developer, you, and this project)

The application can be extended with the following use cases:

  • Search Articles by Search Term
  • Filter Articles by Country, Category, or Source

The application should take advantage of built-in features of the JavaScript framework. Where features do not exist, you can use third-party libraries. Use the framework and any number of libraries to:

  • Implement navigation to different screens
  • Handle XMLHttpRequests (requests to the News API server)
  • Create components that communicate with each other (for example, the component that allows the user to filter articles could communicate with the list component)
  • Implement styles

If you get stuck, post in the forum to ask for help from your tutor and peers. Check in and see if any of your peers need help.

Once you have completed your single-page application, share it in the forum.

Module Linking
Main Topic Image
A close view of a developer's IDE in a dimly lit room
Is Study Guide?
Off
Is Assessment Consultation?
Off