Fetching paginated data using React.js

We’re prepared to fetch data from the Hacker News API. Take a closer look at the data structure and observe how the Hacker News API returns more than a list of hits.

Key points

#N1
Development team

Fetching paginated data using React.js

Paginated Fetch

We’re prepared to fetch data from the Hacker News API. Take a closer look at the data structure and observe how the Hacker News API returns more than a list of hits. Precisely it returns a paginated list. The page property, which is 0 in the first response, can be used to fetch more paginated sublists as results. You only need to pass the next page with the same search term to the API. Let’s extend the composable API constants so it can deal with paginated data:

src/App.js

const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';

Now you can use the new constant to add the page parameter to your API request:

Code Playground

const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}&${PARAM_PAGE}`;
console.log(url);
// output: https://hn.algolia.com/api/v1/search?query=redux&page=

The fetchSearchTopStories() method will take the page as second argument. If you don’t provide the second argument, it will fallback to the 0 page for the initial request. Thus the componentDidMount() and onSearchSubmit() methods fetch the first page on the first request. Every additional fetch should fetch the next page by providing the second argument.

src/App.js

class App extends Component {
  ...
  fetchSearchTopStories(searchTerm, page = 0) {
    fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}`)
        .then(response => response.json())
        .then(result => this.setSearchTopStories(result))
        .catch(error => error);
  }
  ...
}

The page argument uses the JavaScript ES6 default parameter to introduce the fallback to page 0 in case no defined page argument is provided for the function.

Now you can use the current page from the API response in fetchSearchTopStories(). You can use this method in a button to fetch more stories with an onClick button handler. Let’s use the Button to fetch more paginated data from the Hacker News API. For this, we’ll define the onClick() handler, which takes the current search term and the next page (current page + 1).

src/App.js

class App extends Component {

    ...
  render() {
    const { searchTerm, result } = this.state;
    const page = (result && result.page) || 0;
    
    return (
      <div className="page">
        <div className="interactions">
          ...
          { result &&
            <Table
              list={result.hits}
              onDismiss={this.onDismiss}
            />
          }
          <div className="interactions">
            <Button onClick={() => this.fetchSearchTopStories(searchTerm, page + 1)}>
              More
            </Button>
          </div>
      </div>
    );
  }
}

In your render() method, make sure to default to page 0 when there is no result. Remember, the render() method is called before the data is fetched asynchronously in the componentDidMount() lifecycle method.

There is still one step missing, because fetching the next page of data will override your previous page of data. We want to concatenate the old and new list of hits from the local state and new result object, so we’ll adjust its functionality to add new data rather than override it.

src/App.js

setSearchTopStories(result) {
  const { hits, page } = result;
  const oldHits = page !== 0
                    ? this.state.result.hits
                    : [];
  const updatedHits = [
    ...oldHits,
    ...hits
  ];

  this.setState({
    result: { hits: updatedHits, page }
  });

}

A couple things happen in the setSearchTopStories() method now. First, you get the hits and page from the result.

Second, you have to check if there are already old hits. When the page is 0, it is a new search request from componentDidMount() or onSearchSubmit(). The hits are empty. But when you click the “More” button to fetch paginated data the page isn’t 0. The old hits are already stored in your state and thus can be used.

Third, you don’t want to override the old hits. You can merge old and new hits from the recent API request, which can be done with a JavaScript ES6 array spread operator. Fourth, you set the merged hits and page in the local component state.

Now we’ll make one last adjustment. When you try the “More” button it only fetches a few list items. The API URL can be extended to fetch more list items with each request, so we add even more composable path constants:

src/App.js

const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '100';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';
const PARAM_HPP = 'hitsPerPage=';

Now you can use the constants to extend the API URL.

src/App.js

fetchSearchTopStories(searchTerm, page = 0) {
// be careful with the "\" which shows up in the PDF/print version of the book
// it's only a line break a should not be in the actual code
// https://github.com/the-road-to-learn-react/the-road-to-learn-react/issues/43
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
    .then(response => response.json())
    .then(result => this.setSearchTopStories(result))
    .catch(error => error);
}

The request to the Hacker News API fetches more list items in one request than before. As you can see, a powerful API such as the Hacker News API gives plenty of ways to experiment with real world data. You should make use of it to make your endeavours when learning something new more exciting. That’s how I learned about the empowerment that APIs provide when learning a new programming language or library

Completely Example

src/App.js

import React, { Component } from 'react';
import './App.css';
import Table from './Table.js';
import Button from './Button.js';

const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '100';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';
const PARAM_HPP = 'hitsPerPage=';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      result: null,
      searchTerm: DEFAULT_QUERY,
    };
    this.setSearchTopStories = this.setSearchTopStories.bind(this);
    this.onDismiss = this.onDismiss.bind(this);
  }

  setSearchTopStories(result) {
    this.setState({ result });
  }

  onDismiss(id) {
    const isNotId = item => item.objectID !== id;
    const updatedHits = this.state.result.hits.filter(isNotId);
    this.setState({
    result: Object.assign({}, this.state.result, { hits: updatedHits })
    });
  }


  fetchSearchTopStories(searchTerm, page = 0) {
    fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
    .then(response => response.json())
    .then(result => this.setSearchTopStories(result))
    .catch(error => error);
  }


  componentDidMount() {
    this.fetchSearchTopStories("redux"); 
  }

  render() {
    const { searchTerm, result } = this.state;
    const page = (result && result.page) || 0;
    
    return (
      <div className="page">

        { result &&
            <Table
              list={result.hits}
              onDismiss={this.onDismiss}
            />
        }

        <div className="interactions">
          <Button onClick={() => this.fetchSearchTopStories(searchTerm, page + 1)}>
            More
          </Button>
        </div>

      </div>
    );
  }
}

export default App;

src/Button.js

import React, { Component } from 'react';

class Button extends Component {
  render() {
    const {
      onClick,
      className,
      children,
    } = this.props;

    return (
      <button
        onClick={onClick}
        className={className}
        type="button"
      >
      {children}
      </button>
    );
  }
}

export default Button;

src/Table.js

import React, { Component } from 'react';
import Button from './Button.js';

class Table extends Component {
  render() {
    const { list, pattern, onDismiss } = this.props;
    return (
      <div>
        {list.map(item =>
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
            <span>
              <Button onClick={() => onDismiss(item.objectID)}>
                Dismiss
              </Button>
            </span>
          </div>
        )}
      </div>
    );
  }
}

export default Table;

Output :

Example hacker news api data

{
  "exhaustive": {
    "nbHits": false,
    "typo": false
  },
  "exhaustiveNbHits": false,
  "exhaustiveTypo": false,
  "hits": [
    {
      "_highlightResult": {
        "author": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "DerWOK"
        },
        "title": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "Guerrilla Public Service <em>Redux</em> (2017)"
        },
        "url": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "https://99percentinvisible.org/episode/guerrilla-public-service/"
        }
      },
      "_tags": [
        "story",
        "author_DerWOK",
        "story_23325319"
      ],
      "author": "DerWOK",
      "children": [
        23325398,
        23325627,
        23325634,
        23325741,
        23325759,
        23325791,
        23325887,
        23325921,
        23326091,
        23326133,
        23326867,
        23327040,
        23327202,
        23327520,
        23327772,
        23327836,
        23328127,
        23328230,
        23328277,
        23328720,
        23329322,
        23330049,
        23331326,
        23337295
      ],
      "created_at": "2020-05-27T16:05:32Z",
      "created_at_i": 1590595532,
      "num_comments": 110,
      "objectID": "23325319",
      "points": 421,
      "story_id": 23325319,
      "title": "Guerrilla Public Service Redux (2017)",
      "updated_at": "2023-09-07T06:50:57Z",
      "url": "https://99percentinvisible.org/episode/guerrilla-public-service/"
    },
    {
      "_highlightResult": {
        "author": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "jdeal"
        },
        "title": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "Build Yourself a <em>Redux</em>"
        },
        "url": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "https://zapier.com/engineering/how-to-build-<em>redux</em>/"
        }
      },
      "_tags": [
        "story",
        "author_jdeal",
        "story_14273549"
      ],
      "author": "jdeal",
      "children": [
        14273685,
        14273853,
        14273902,
        14273922,
        14273957,
        14274103,
        14274112,
        14274459,
        14274784,
        14274902,
        14275010,
        14275348,
        14275378,
        14275582,
        14275979,
        14276068,
        14276094,
        14278325,
        14279322
      ],
      "created_at": "2017-05-05T14:14:17Z",
      "created_at_i": 1493993657,
      "num_comments": 155,
      "objectID": "14273549",
      "points": 395,
      "story_id": 14273549,
      "title": "Build Yourself a Redux",
      "updated_at": "2023-09-07T02:29:29Z",
      "url": "https://zapier.com/engineering/how-to-build-redux/"
    }
  ],
  "hitsPerPage": 2,
  "nbHits": 18985,
  "nbPages": 500,
  "page": 0,
  "params": "query=redux&page=0&hitsPerPage=2&advancedSyntax=true&analyticsTags=backend",
  "processingTimeMS": 7,
  "processingTimingsMS": {
    "_request": {
      "roundTrip": 13
    },
    "fetch": {
      "query": 4,
      "scanning": 1,
      "total": 6
    },
    "total": 7
  },
  "query": "redux",
  "serverTimeMS": 7
}

#N2
Development team

Installation

Installation

There are many approaches to getting started with a React application. The first we’ll explore is a CDN, short for content delivery network. Many companies use CDNs to host files publicly for their consumers. Some of these files are libraries like React, since the bundled React library is just a react.js JavaScript file. To get started with React by using a CDN, find the <script> tag in your web page HTML that points to a CDN url.

You will need two libraries: react and react-dom.

Code Playground

<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>

You can also get React into your application by initializing it as node project. With a package.json file, you can install react and react-dom from the command line. However, the folder must be initialized as a npm project using npm init -y with a package.json file. You can install multiple node packages with npm:

Command Line

npm install react react-dom

This approach is often used to add React to an existing application managed with npm. You may also have to deal with Babel to make your application aware of JSX (the React syntax) and JavaScript ES6. Babel transpiles your code–that is, it converts it to vanilla JavaScript–so most modern browsers can interpret JavaScript ES6 and JSX. Because of this difficult setup, Facebook introduced create-react-app as a zero-configuration React solution. The next section will show you how to setup your application using this bootstrapping tool.

Zero-Configuration Setup

we can use create-react-app to bootstrap your application. It’s an opinionated yet zero-configuration starter kit for React introduced by Facebook in 2016, recommended for beginners by 96% of React users. In create-react-app the tooling and configuration evolve in the background, while the focus is on the application implementation.

To get started, install the package to your global node packages, which keeps it available on the command line to bootstrap new React applications:

Command Line

npm install -g create-react-app

You can check the version of create-react-app to verify a successful installation on your command line:

Command Line

create-react-app --version
*v5.0.1

Now you are ready to bootstrap your first React application. The example will be referred to as hackernews, but you may choose any name you like. First, navigate into the folder:

Command Line

create-react-app hackernews
cd hackernews
npm install react react-dom

Now you can open the application in your editor. The following folder structure, or a variation of it depending on the create-react-app version, should be presented to you:

Folder Structure

hackernews/
    README.md
    node_modules/
    package.json
    .gitignore
    public/
      favicon.ico
      index.html
      manifest.json 

    src/
      App.css
      App.js
      App.test.js
      index.css
      index.js
      logo.svg
      serviceWorker.js

This is a breakdown of the folders and files:

  • README.md: The .md extension indicates the file is a markdown file. Markdown is used as a lightweight markup language with plain text formatting syntax. Many source code projects come with a README.md file to give you initial instructions about the project. When pushing your project to a platform such as GitHub, the README.md file usually displays information about the content contained in the repository. Because you used create-react-app, your README.md should be the same as the official create-react-app GitHub repository⁴⁹.

  • node_modules/: This folder contains all node packages that have been installed via npm. Since you used create-react-app, there should already be a couple of node modules installed for you. You will rarely touch this folder, because node packages are generally installed and uninstalled with npm from the command line.

  • package.json: This file shows you a list of node package dependencies and other project configurations.

  • .gitignore: This file displays all files and folders that shouldn’t be added to your git repository when using git; such files and folders should only be located in your local project. The node_- modules/ folder is one example. It is enough to share the package.json file with others, so they can install dependencies on their end with npm install without your dependency folder.

  • public/: This folder holds development files, such as public/index.html. The index is displayed on localhost:3000 when developing your app. The boilerplate takes care of relating this index with all the scripts in src/.

  • build/ This folder is created when you build the project for production, as it holds all of the production files. When building your project for production, all the source code in the src/ and public/ folders are bundled and placed in the build folder.

  • manifest.json and serviceWorker.js: These files won’t be used for this project, so you can ignore them for now.

In the beginning, everything you need is located in the src/ folder. The main focus lies on the src/App.js file which is used to implement React components. It will be used to implement your application, but later you might want to split up your components into multiple files, where each file maintains one or more components on its own.

Additionally, you will find a src/App.test.js file for your tests, and a src/index.js as an entry point to the React world. You will get to know both files intimately in a later chapter. There is also a src/index.css and a src/App.css file to style your general application and components, which comes with the default style when you open them. You will modify them later as well. The create-react-app application is a npm project you can use to install and uninstall node packages. It comes with the following npm scripts for your command line:

Command Line

Runs the application in http://localhost:3000

npm start

Runs the tests

npm test

Builds the application for production

npm run build

The scripts are defined in your package.json, and your basic React application is bootstrapped.

#C1
Development team

Fetching paginated data using React.js

Paginated Fetch

We’re prepared to fetch data from the Hacker News API. Take a closer look at the data structure and observe how the Hacker News API returns more than a list of hits. Precisely it returns a paginated list. The page property, which is 0 in the first response, can be used to fetch more paginated sublists as results. You only need to pass the next page with the same search term to the API. Let’s extend the composable API constants so it can deal with paginated data:

src/App.js

const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';

Now you can use the new constant to add the page parameter to your API request:

Code Playground

const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}&${PARAM_PAGE}`;
console.log(url);
// output: https://hn.algolia.com/api/v1/search?query=redux&page=

The fetchSearchTopStories() method will take the page as second argument. If you don’t provide the second argument, it will fallback to the 0 page for the initial request. Thus the componentDidMount() and onSearchSubmit() methods fetch the first page on the first request. Every additional fetch should fetch the next page by providing the second argument.

src/App.js

class App extends Component {
  ...
  fetchSearchTopStories(searchTerm, page = 0) {
    fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}`)
        .then(response => response.json())
        .then(result => this.setSearchTopStories(result))
        .catch(error => error);
  }
  ...
}

The page argument uses the JavaScript ES6 default parameter to introduce the fallback to page 0 in case no defined page argument is provided for the function.

Now you can use the current page from the API response in fetchSearchTopStories(). You can use this method in a button to fetch more stories with an onClick button handler. Let’s use the Button to fetch more paginated data from the Hacker News API. For this, we’ll define the onClick() handler, which takes the current search term and the next page (current page + 1).

src/App.js

class App extends Component {

    ...
  render() {
    const { searchTerm, result } = this.state;
    const page = (result && result.page) || 0;
    
    return (
      <div className="page">
        <div className="interactions">
          ...
          { result &&
            <Table
              list={result.hits}
              onDismiss={this.onDismiss}
            />
          }
          <div className="interactions">
            <Button onClick={() => this.fetchSearchTopStories(searchTerm, page + 1)}>
              More
            </Button>
          </div>
      </div>
    );
  }
}

In your render() method, make sure to default to page 0 when there is no result. Remember, the render() method is called before the data is fetched asynchronously in the componentDidMount() lifecycle method.

There is still one step missing, because fetching the next page of data will override your previous page of data. We want to concatenate the old and new list of hits from the local state and new result object, so we’ll adjust its functionality to add new data rather than override it.

src/App.js

setSearchTopStories(result) {
  const { hits, page } = result;
  const oldHits = page !== 0
                    ? this.state.result.hits
                    : [];
  const updatedHits = [
    ...oldHits,
    ...hits
  ];

  this.setState({
    result: { hits: updatedHits, page }
  });

}

A couple things happen in the setSearchTopStories() method now. First, you get the hits and page from the result.

Second, you have to check if there are already old hits. When the page is 0, it is a new search request from componentDidMount() or onSearchSubmit(). The hits are empty. But when you click the “More” button to fetch paginated data the page isn’t 0. The old hits are already stored in your state and thus can be used.

Third, you don’t want to override the old hits. You can merge old and new hits from the recent API request, which can be done with a JavaScript ES6 array spread operator. Fourth, you set the merged hits and page in the local component state.

Now we’ll make one last adjustment. When you try the “More” button it only fetches a few list items. The API URL can be extended to fetch more list items with each request, so we add even more composable path constants:

src/App.js

const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '100';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';
const PARAM_HPP = 'hitsPerPage=';

Now you can use the constants to extend the API URL.

src/App.js

fetchSearchTopStories(searchTerm, page = 0) {
// be careful with the "\" which shows up in the PDF/print version of the book
// it's only a line break a should not be in the actual code
// https://github.com/the-road-to-learn-react/the-road-to-learn-react/issues/43
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
    .then(response => response.json())
    .then(result => this.setSearchTopStories(result))
    .catch(error => error);
}

The request to the Hacker News API fetches more list items in one request than before. As you can see, a powerful API such as the Hacker News API gives plenty of ways to experiment with real world data. You should make use of it to make your endeavours when learning something new more exciting. That’s how I learned about the empowerment that APIs provide when learning a new programming language or library

Completely Example

src/App.js

import React, { Component } from 'react';
import './App.css';
import Table from './Table.js';
import Button from './Button.js';

const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '100';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';
const PARAM_HPP = 'hitsPerPage=';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      result: null,
      searchTerm: DEFAULT_QUERY,
    };
    this.setSearchTopStories = this.setSearchTopStories.bind(this);
    this.onDismiss = this.onDismiss.bind(this);
  }

  setSearchTopStories(result) {
    this.setState({ result });
  }

  onDismiss(id) {
    const isNotId = item => item.objectID !== id;
    const updatedHits = this.state.result.hits.filter(isNotId);
    this.setState({
    result: Object.assign({}, this.state.result, { hits: updatedHits })
    });
  }


  fetchSearchTopStories(searchTerm, page = 0) {
    fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
    .then(response => response.json())
    .then(result => this.setSearchTopStories(result))
    .catch(error => error);
  }


  componentDidMount() {
    this.fetchSearchTopStories("redux"); 
  }

  render() {
    const { searchTerm, result } = this.state;
    const page = (result && result.page) || 0;
    
    return (
      <div className="page">

        { result &&
            <Table
              list={result.hits}
              onDismiss={this.onDismiss}
            />
        }

        <div className="interactions">
          <Button onClick={() => this.fetchSearchTopStories(searchTerm, page + 1)}>
            More
          </Button>
        </div>

      </div>
    );
  }
}

export default App;

src/Button.js

import React, { Component } from 'react';

class Button extends Component {
  render() {
    const {
      onClick,
      className,
      children,
    } = this.props;

    return (
      <button
        onClick={onClick}
        className={className}
        type="button"
      >
      {children}
      </button>
    );
  }
}

export default Button;

src/Table.js

import React, { Component } from 'react';
import Button from './Button.js';

class Table extends Component {
  render() {
    const { list, pattern, onDismiss } = this.props;
    return (
      <div>
        {list.map(item =>
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
            <span>
              <Button onClick={() => onDismiss(item.objectID)}>
                Dismiss
              </Button>
            </span>
          </div>
        )}
      </div>
    );
  }
}

export default Table;

Output :

Example hacker news api data

{
  "exhaustive": {
    "nbHits": false,
    "typo": false
  },
  "exhaustiveNbHits": false,
  "exhaustiveTypo": false,
  "hits": [
    {
      "_highlightResult": {
        "author": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "DerWOK"
        },
        "title": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "Guerrilla Public Service <em>Redux</em> (2017)"
        },
        "url": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "https://99percentinvisible.org/episode/guerrilla-public-service/"
        }
      },
      "_tags": [
        "story",
        "author_DerWOK",
        "story_23325319"
      ],
      "author": "DerWOK",
      "children": [
        23325398,
        23325627,
        23325634,
        23325741,
        23325759,
        23325791,
        23325887,
        23325921,
        23326091,
        23326133,
        23326867,
        23327040,
        23327202,
        23327520,
        23327772,
        23327836,
        23328127,
        23328230,
        23328277,
        23328720,
        23329322,
        23330049,
        23331326,
        23337295
      ],
      "created_at": "2020-05-27T16:05:32Z",
      "created_at_i": 1590595532,
      "num_comments": 110,
      "objectID": "23325319",
      "points": 421,
      "story_id": 23325319,
      "title": "Guerrilla Public Service Redux (2017)",
      "updated_at": "2023-09-07T06:50:57Z",
      "url": "https://99percentinvisible.org/episode/guerrilla-public-service/"
    },
    {
      "_highlightResult": {
        "author": {
          "matchLevel": "none",
          "matchedWords": [],
          "value": "jdeal"
        },
        "title": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "Build Yourself a <em>Redux</em>"
        },
        "url": {
          "fullyHighlighted": false,
          "matchLevel": "full",
          "matchedWords": [
            "redux"
          ],
          "value": "https://zapier.com/engineering/how-to-build-<em>redux</em>/"
        }
      },
      "_tags": [
        "story",
        "author_jdeal",
        "story_14273549"
      ],
      "author": "jdeal",
      "children": [
        14273685,
        14273853,
        14273902,
        14273922,
        14273957,
        14274103,
        14274112,
        14274459,
        14274784,
        14274902,
        14275010,
        14275348,
        14275378,
        14275582,
        14275979,
        14276068,
        14276094,
        14278325,
        14279322
      ],
      "created_at": "2017-05-05T14:14:17Z",
      "created_at_i": 1493993657,
      "num_comments": 155,
      "objectID": "14273549",
      "points": 395,
      "story_id": 14273549,
      "title": "Build Yourself a Redux",
      "updated_at": "2023-09-07T02:29:29Z",
      "url": "https://zapier.com/engineering/how-to-build-redux/"
    }
  ],
  "hitsPerPage": 2,
  "nbHits": 18985,
  "nbPages": 500,
  "page": 0,
  "params": "query=redux&page=0&hitsPerPage=2&advancedSyntax=true&analyticsTags=backend",
  "processingTimeMS": 7,
  "processingTimingsMS": {
    "_request": {
      "roundTrip": 13
    },
    "fetch": {
      "query": 4,
      "scanning": 1,
      "total": 6
    },
    "total": 7
  },
  "query": "redux",
  "serverTimeMS": 7
}

#C2
Development team

Installation

Installation

There are many approaches to getting started with a React application. The first we’ll explore is a CDN, short for content delivery network. Many companies use CDNs to host files publicly for their consumers. Some of these files are libraries like React, since the bundled React library is just a react.js JavaScript file. To get started with React by using a CDN, find the <script> tag in your web page HTML that points to a CDN url.

You will need two libraries: react and react-dom.

Code Playground

<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>

You can also get React into your application by initializing it as node project. With a package.json file, you can install react and react-dom from the command line. However, the folder must be initialized as a npm project using npm init -y with a package.json file. You can install multiple node packages with npm:

Command Line

npm install react react-dom

This approach is often used to add React to an existing application managed with npm. You may also have to deal with Babel to make your application aware of JSX (the React syntax) and JavaScript ES6. Babel transpiles your code–that is, it converts it to vanilla JavaScript–so most modern browsers can interpret JavaScript ES6 and JSX. Because of this difficult setup, Facebook introduced create-react-app as a zero-configuration React solution. The next section will show you how to setup your application using this bootstrapping tool.

Zero-Configuration Setup

we can use create-react-app to bootstrap your application. It’s an opinionated yet zero-configuration starter kit for React introduced by Facebook in 2016, recommended for beginners by 96% of React users. In create-react-app the tooling and configuration evolve in the background, while the focus is on the application implementation.

To get started, install the package to your global node packages, which keeps it available on the command line to bootstrap new React applications:

Command Line

npm install -g create-react-app

You can check the version of create-react-app to verify a successful installation on your command line:

Command Line

create-react-app --version
*v5.0.1

Now you are ready to bootstrap your first React application. The example will be referred to as hackernews, but you may choose any name you like. First, navigate into the folder:

Command Line

create-react-app hackernews
cd hackernews
npm install react react-dom

Now you can open the application in your editor. The following folder structure, or a variation of it depending on the create-react-app version, should be presented to you:

Folder Structure

hackernews/
    README.md
    node_modules/
    package.json
    .gitignore
    public/
      favicon.ico
      index.html
      manifest.json 

    src/
      App.css
      App.js
      App.test.js
      index.css
      index.js
      logo.svg
      serviceWorker.js

This is a breakdown of the folders and files:

  • README.md: The .md extension indicates the file is a markdown file. Markdown is used as a lightweight markup language with plain text formatting syntax. Many source code projects come with a README.md file to give you initial instructions about the project. When pushing your project to a platform such as GitHub, the README.md file usually displays information about the content contained in the repository. Because you used create-react-app, your README.md should be the same as the official create-react-app GitHub repository⁴⁹.

  • node_modules/: This folder contains all node packages that have been installed via npm. Since you used create-react-app, there should already be a couple of node modules installed for you. You will rarely touch this folder, because node packages are generally installed and uninstalled with npm from the command line.

  • package.json: This file shows you a list of node package dependencies and other project configurations.

  • .gitignore: This file displays all files and folders that shouldn’t be added to your git repository when using git; such files and folders should only be located in your local project. The node_- modules/ folder is one example. It is enough to share the package.json file with others, so they can install dependencies on their end with npm install without your dependency folder.

  • public/: This folder holds development files, such as public/index.html. The index is displayed on localhost:3000 when developing your app. The boilerplate takes care of relating this index with all the scripts in src/.

  • build/ This folder is created when you build the project for production, as it holds all of the production files. When building your project for production, all the source code in the src/ and public/ folders are bundled and placed in the build folder.

  • manifest.json and serviceWorker.js: These files won’t be used for this project, so you can ignore them for now.

In the beginning, everything you need is located in the src/ folder. The main focus lies on the src/App.js file which is used to implement React components. It will be used to implement your application, but later you might want to split up your components into multiple files, where each file maintains one or more components on its own.

Additionally, you will find a src/App.test.js file for your tests, and a src/index.js as an entry point to the React world. You will get to know both files intimately in a later chapter. There is also a src/index.css and a src/App.css file to style your general application and components, which comes with the default style when you open them. You will modify them later as well. The create-react-app application is a npm project you can use to install and uninstall node packages. It comes with the following npm scripts for your command line:

Command Line

Runs the application in http://localhost:3000

npm start

Runs the tests

npm test

Builds the application for production

npm run build

The scripts are defined in your package.json, and your basic React application is bootstrapped.

README.md

A utility-first CSS framework for rapidly building custom user interfaces.

build
passing
downloads
8.7M

Documentation


For full documentation, visit tailwindcss.com

Community


For help, discussion about best practices, or any other conversation that would benefit from being searchable:
Discuss Tailwind CSS on GitHub
For casual chit-chat with others using the framework:
Join the Tailwind CSS Discord Server

Contributing


If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.

© 2020 GitHub, Inc.,