Categories
React

How to Use Async and Await in React Apps

React is a simple library for create interactive front end web apps. Its feature set is basic. It provides you with a component based architecture for building web apps. Each component does a small thing in an app, and they can be nested in each other or put side by side. It supports all the latest JavaScript, including one very handy one, async and await . async and await is the new syntax for chaining promises, so instead of call then callbacks on each promise and returning a new promise in them, we use async and await like so:

const asyncFunction = async () => {
  const promise1Result = await promsise1;
  console.log(promise1Result);
  const promise2Result = await promsise2;
  console.log(promise2Result);
}

The code above is the same as:

const asyncFunction = () => {
  promise1
  .then((promise1Result) => {
    console.log(promise1Result);
    return promise2
  })
  .then((promise2Result) => {
    console.log(promise2Result);
  })
}

For form validation, then you need to use a third party library. Formik and Yup , located at https://github.com/jaredpalmer/formik and https://github.com/jquense/yup work great together to allow us to take care of most form validation needs. Formik let us build the forms and display the errors, and handle form value changes, which is another thing we have to do all my hand otherwise. Yup let us write a schema for validating our form fields. It can check almost anything, with common validation code like email and required fields available as built in functions. It can also check for fields that depend on other fields, like the postal code format depending on country. Bootstrap forms can be used seamlessly with Formik and Yup.

In this story, we will build an address book app, which uses those libraries, plus React Bootstrap, which has great integration with those libraries above to create forms. To start we need to run Create React App to scaffold the app. We run npx create-react-app address-book to create the app project folder with the initial files. The app will have a home page to display the contacts and let us open a modal to add a contact. There will be a table that displays all the contacts and Edit and Delete buttons on each row to edit or delete each contact. The contacts will store in a central Redux store to store the contacts in a central place, making them easy to access. React Router will be used for routing. Contacts will be saved in the back end spawned using the JSON server package, located at https://github.com/typicode/json-server.

Once that is done, we have to install some libraries. To install the libraries we mentioned above, we run npm i axios bootstrap formik react-bootstrap react-redux react-router-dom yup . Axios is the HTTP client that we use for making HTTP requests to back end. react-router-dom is the package name for the latest version of React Router.

Now that we have all the libraries installed, we can start building the app. All files will be in the src folder except mentioned otherwise. First we work on the Redux store. We create a file called actionCreator.js in the src folder and add the following:

import { SET_CONTACTS } from './actions';

const setContacts = (contacts) => {
    return {
        type: SET_CONTACTS,
        payload: contacts
    }
};

export { setContacts };

This is the action creator for creating the action for storing the contacts in the store.

We create another file called actions.js and add:

const SET_CONTACTS = 'SET_CONTACTS';

export { SET_CONTACTS };

This just have the type constant for dispatching the action.

In App.js , we replace what is existing with the following:

import React from 'react';
import { Router, Route, Link } from "react-router-dom";
import HomePage from './HomePage';
import { createBrowserHistory as createHistory } from 'history'
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import './App.css';
const history = createHistory();

function App() {
  return (
    <div className="App">
      <Router history={history}>
        <Navbar bg="primary" expand="lg" variant="dark" >
          <Navbar.Brand href="#home">Address Book App</Navbar.Brand>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Nav className="mr-auto">
              <Nav.Link href="/">Home</Nav.Link>
            </Nav>
          </Navbar.Collapse>
        </Navbar>
        <Route path="/" exact component={HomePage} />
      </Router>
    </div>
  );
}

export default App;

This is where we add the navigation bar and show our routes routed by the React Router. In App.css , we replace the existing code with:

.App {
  text-align: center;
}

to center some text.

Next we build our contact form. This is the most logic heavy part of our app. We create a file called ContactForm.js and add:

import React from 'react';
import { Formik } from 'formik';
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import InputGroup from 'react-bootstrap/InputGroup';
import Button from 'react-bootstrap/Button';
import * as yup from 'yup';
import { COUNTRIES } from './exports';
import PropTypes from 'prop-types';
import { addContact, editContact, getContacts } from './requests';
import { connect } from 'react-redux';
import { setContacts } from './actionCreators';

const schema = yup.object({
  firstName: yup.string().required('First name is required'),
  lastName: yup.string().required('Last name is required'),
  address: yup.string().required('Address is required'),
  city: yup.string().required('City is required'),
  region: yup.string().required('Region is required'),
  country: yup.string().required('Country is required').default('Afghanistan'),
  postalCode: yup
    .string()
    .when('country', {
      is: 'United States',
      then: yup.string().matches(/^[0-9]{5}(?:-[0-9]{4})?$/, 'Invalid postal code'),
    })
    .when('country', {
      is: 'Canada',
      then: yup.string().matches(/^[A-Za-z]d[A-Za-z][ -]?d[A-Za-z]d$/, 'Invalid postal code'),
    })
    .required(),
  phone: yup
    .string()
    .when('country', {
      is: country => ["United States", "Canada"].includes(country),
      then: yup.string().matches(/^[2-9]d{2}[2-9]d{2}d{4}$/, 'Invalid phone nunber')
    })
    .required(),
  email: yup.string().email('Invalid email').required('Email is required'),
  age: yup.number()
    .required('Age is required')
    .min(0, 'Minimum age is 0')
    .max(200, 'Maximum age is 200'),
});

function ContactForm({
  edit,
  onSave,
  setContacts,
  contact,
  onCancelAdd,
  onCancelEdit,
}) {
  const handleSubmit = async (evt) => {
    const isValid = await schema.validate(evt);
    if (!isValid) {
      return;
    }
    if (!edit) {
      await addContact(evt);
    }
    else {
      await editContact(evt);
    }
    const response = await getContacts();
    setContacts(response.data);
    onSave();
  }

  return (
    <div className="form">
      <Formik
        validationSchema={schema}
        onSubmit={handleSubmit}
        initialValues={contact || {}}
      >
        {({
          handleSubmit,
          handleChange,
          handleBlur,
          values,
          touched,
          isInvalid,
          errors,
        }) => (
            <Form noValidate onSubmit={handleSubmit}>
              <Form.Row>
                <Form.Group as={Col} md="12" controlId="firstName">
                  <Form.Label>First name</Form.Label>
                  <Form.Control
                    type="text"
                    name="firstName"
                    placeholder="First Name"
                    value={values.firstName || ''}
                    onChange={handleChange}
                    isInvalid={touched.firstName && errors.firstName}
                  />
                  <Form.Control.Feedback type="invalid">
                    {errors.firstName}
                  </Form.Control.Feedback>
                </Form.Group>
                <Form.Group as={Col} md="12" controlId="lastName">
                  <Form.Label>Last name</Form.Label>
                  <Form.Control
                    type="text"
                    name="lastName"
                    placeholder="Last Name"
                    value={values.lastName || ''}
                    onChange={handleChange}
                    isInvalid={touched.firstName && errors.lastName}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.lastName}
                  </Form.Control.Feedback>
                </Form.Group>
                <Form.Group as={Col} md="12" controlId="address">
                  <Form.Label>Address</Form.Label>
                  <InputGroup>
                    <Form.Control
                      type="text"
                      placeholder="Address"
                      aria-describedby="inputGroupPrepend"
                      name="address"
                      value={values.address || ''}
                      onChange={handleChange}
                      isInvalid={touched.address && errors.address}
                    />
                    <Form.Control.Feedback type="invalid">
                      {errors.address}
                    </Form.Control.Feedback>
                  </InputGroup>
                </Form.Group>
              </Form.Row>
              <Form.Row>
                <Form.Group as={Col} md="12" controlId="city">
                  <Form.Label>City</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="City"
                    name="city"
                    value={values.city || ''}
                    onChange={handleChange}
                    isInvalid={touched.city && errors.city}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.city}
                  </Form.Control.Feedback>
                </Form.Group>
                <Form.Group as={Col} md="12" controlId="region">
                  <Form.Label>Region</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="Region"
                    name="region"
                    value={values.region || ''}
                    onChange={handleChange}
                    isInvalid={touched.region && errors.region}
                  />
                  <Form.Control.Feedback type="invalid">
                    {errors.region}
                  </Form.Control.Feedback>
                </Form.Group>

                <Form.Group as={Col} md="12" controlId="country">
                  <Form.Label>Country</Form.Label>
                  <Form.Control
                    as="select"
                    placeholder="Country"
                    name="country"
                    onChange={handleChange}
                    value={values.country || ''}
                    isInvalid={touched.region && errors.country}>
                    {COUNTRIES.map(c => <option key={c} value={c}>{c}</option>)}
                  </Form.Control>
                  <Form.Control.Feedback type="invalid">
                    {errors.country}
                  </Form.Control.Feedback>
                </Form.Group>

                <Form.Group as={Col} md="12" controlId="postalCode">
                  <Form.Label>Postal Code</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="Postal Code"
                    name="postalCode"
                    value={values.postalCode || ''}
                    onChange={handleChange}
                    isInvalid={touched.postalCode && errors.postalCode}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.postalCode}
                  </Form.Control.Feedback>
                </Form.Group>

                <Form.Group as={Col} md="12" controlId="phone">
                  <Form.Label>Phone</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="Phone"
                    name="phone"
                    value={values.phone || ''}
                    onChange={handleChange}
                    isInvalid={touched.phone && errors.phone}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.phone}
                  </Form.Control.Feedback>
                </Form.Group>

                <Form.Group as={Col} md="12" controlId="email">
                  <Form.Label>Email</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="Email"
                    name="email"
                    value={values.email || ''}
                    onChange={handleChange}
                    isInvalid={touched.email && errors.email}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.email}
                  </Form.Control.Feedback>
                </Form.Group>

                <Form.Group as={Col} md="12" controlId="age">
                  <Form.Label>Age</Form.Label>
                  <Form.Control
                    type="text"
                    placeholder="Age"
                    name="age"
                    value={values.age || ''}
                    onChange={handleChange}
                    isInvalid={touched.age && errors.age}
                  />

                  <Form.Control.Feedback type="invalid">
                    {errors.age}
                  </Form.Control.Feedback>
                </Form.Group>
              </Form.Row>
              <Button type="submit" style={{ 'marginRight': '10px' }}>Save</Button>
              <Button type="button" onClick={edit ? onCancelEdit : onCancelAdd}>Cancel</Button>
            </Form>
          )}
      </Formik>
    </div>
  );
}

ContactForm.propTypes = {
  edit: PropTypes.bool,
  onSave: PropTypes.func,
  onCancelAdd: PropTypes.func,
  onCancelEdit: PropTypes.func,
  contact: PropTypes.object
}

const mapStateToProps = state => {
  return {
    contacts: state.contacts,
  }
}

const mapDispatchToProps = dispatch => ({
  setContacts: contacts => dispatch(setContacts(contacts))
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ContactForm);

We use Formik to facilitate building our contact form here, with our Boostrap Form component nested in the Formik component so that we can use Formik’s handleChange , handleSubmit , values , touched and errors parameters. handleChange is a function that let us update the form field data from the inputs without writing the code ourselves. handleSubmit is the function that we passed into the onSubmit handler of the Formik component. The parameter in the function is the data we entered, with the field name as the key, as defined by the name attribute of each field and the value of each field as the value of those keys. Notice that in each value prop, we have ||'' so we do not get undefined values and prevent uncontrolled form warnings from getting triggered.

To display form validation messages, we have to pass in the isInvalid prop to each Form.Control component. The schema object is what Formik will check against for form validation. The argument in the required function is the validation error message. The second argument of the matches , min and max functions are also validation messages.

The parameter of the ContactForm function are props, which we will pass in from the HomePage component that we will build later. The handleSubmit function checks if the data is valid, then if it is then it will proceed to saving according to whether it is adding or editing a contact. Then when saving is successful we set the contacts in the store and call onSave prop, which is a function to close the modal the form is in. The modal will be defined in the home page.

Notice that in the handleSubmit function, we used async and await . It is much shorter than using then callbacks, which would be the alternative. We call a promise for validating the fields, then another one for saving data to back end, and another one to get the latest data.

mapStateToProps is a function provided by React Redux so that we can map the state directly to the props of our component as the function name suggests. mapDispatchToProps allows us to call function in the props of the component called setContacts to dispatch the action as we defined in actionCreators.js

Next we create a file called exports.js , and put:

export const COUNTRIES = ["Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Anguilla", "Antigua &amp; Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas"
    , "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia &amp; Herzegovina", "Botswana", "Brazil", "British Virgin Islands"
    , "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Chad", "Chile", "China", "Colombia", "Congo", "Cook Islands", "Costa Rica"
    , "Cote D Ivoire", "Croatia", "Cruise Ship", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea"
    , "Estonia", "Ethiopia", "Falkland Islands", "Faroe Islands", "Fiji", "Finland", "France", "French Polynesia", "French West Indies", "Gabon", "Gambia", "Georgia", "Germany", "Ghana"
    , "Gibraltar", "Greece", "Greenland", "Grenada", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea Bissau", "Guyana", "Haiti", "Honduras", "Hong Kong", "Hungary", "Iceland", "India"
    , "Indonesia", "Iran", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kuwait", "Kyrgyz Republic", "Laos", "Latvia"
    , "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Mauritania"
    , "Mauritius", "Mexico", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Namibia", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia"
    , "New Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Poland", "Portugal"
    , "Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda", "Saint Pierre &amp; Miquelon", "Samoa", "San Marino", "Satellite", "Saudi Arabia", "Senegal", "Serbia", "Seychelles"
    , "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea", "Spain", "Sri Lanka", "St Kitts &amp; Nevis", "St Lucia", "St Vincent", "St. Lucia", "Sudan"
    , "Suriname", "Swaziland", "Sweden", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor L'Este", "Togo", "Tonga", "Trinidad &amp; Tobago", "Tunisia"
    , "Turkey", "Turkmenistan", "Turks &amp; Caicos", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "United States Minor Outlying Islands", "Uruguay"
    , "Uzbekistan", "Venezuela", "Vietnam", "Virgin Islands (US)", "Yemen", "Zambia", "Zimbabwe"];

These are countries for the countries field in the form.

In HomePage.js , we put:

import React from 'react';
import { useState, useEffect } from 'react';
import Table from 'react-bootstrap/Table'
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal'
import ContactForm from './ContactForm';
import './HomePage.css';
import { connect } from 'react-redux';
import { getContacts, deleteContact } from './requests';

function HomePage() {
  const [openAddModal, setOpenAddModal] = useState(false);
  const [openEditModal, setOpenEditModal] = useState(false);
  const [initialized, setInitialized] = useState(false);
  const [selectedId, setSelectedId] = useState(0);
  const [selectedContact, setSelectedContact] = useState({});
  const [contacts, setContacts] = useState([]);

  const openModal = () => {
    setOpenAddModal(true);
  }

  const closeModal = () => {
    setOpenAddModal(false);
    setOpenEditModal(false);
    getData();
  }

  const cancelAddModal = () => {
    setOpenAddModal(false);
  }

  const editContact = (contact) => {
    setSelectedContact(contact);
    setOpenEditModal(true);
  }

  const cancelEditModal = () => {
    setOpenEditModal(false);
  }

  const getData = async () => {
    const response = await getContacts();
    setContacts(response.data);
    setInitialized(true);
  }

  const deleteSelectedContact = async (id) => {
    await deleteContact(id);
    getData();
  }

  useEffect(() => {
    if (!initialized) {
      getData();
    }
  })

  return (
    <div className="home-page">
      <h1>Contacts</h1>
      <Modal show={openAddModal} onHide={closeModal} >
        <Modal.Header closeButton>
          <Modal.Title>Add Contact</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <ContactForm edit={false} onSave={closeModal.bind(this)} onCancelAdd={cancelAddModal} />
        </Modal.Body>
      </Modal>

      <Modal show={openEditModal} onHide={closeModal}>
        <Modal.Header closeButton>
          <Modal.Title>Edit Contact</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <ContactForm edit={true} onSave={closeModal.bind(this)} contact={selectedContact} onCancelEdit={cancelEditModal} />
        </Modal.Body>
      </Modal>
      <ButtonToolbar onClick={openModal}>
        <Button variant="outline-primary">Add Contact</Button>
      </ButtonToolbar>
      <br />
      <Table striped bordered hover>
        <thead>
          <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Address</th>
            <th>City</th>
            <th>Country</th>
            <th>Postal Code</th>
            <th>Phone</th>
            <th>Email</th>
            <th>Age</th>
            <th>Edit</th>
            <th>Delete</th>
          </tr>
        </thead>
        <tbody>
          {contacts.map(c => (
            <tr key={c.id}>
              <td>{c.firstName}</td>
              <td>{c.lastName}</td>
              <td>{c.address}</td>
              <td>{c.city}</td>
              <td>{c.country}</td>
              <td>{c.postalCode}</td>
              <td>{c.phone}</td>
              <td>{c.email}</td>
              <td>{c.age}</td>
              <td>
                <Button variant="outline-primary" onClick={editContact.bind(this, c)}>Edit</Button>
              </td>
              <td>
                <Button variant="outline-primary" onClick={deleteSelectedContact.bind(this, c.id)}>Delete</Button>
              </td>
            </tr>
          ))}
        </tbody>
      </Table>
    </div>
  );
}

const mapStateToProps = state => {
  return {
    contacts: state.contacts,
  }
}

export default connect(
  mapStateToProps,
  null
)(HomePage);

It has the table for display the contacts and buttons to add, edit, and delete contact. It gets data once on first load with the getData function call in the useEffect ‘s callback function. useEffect ‘s callback is called on every render so we want to set a initialized flag and check that it loads only if it’s true .

useEffect does not support passing in an async function as the callback, so we have to call it inside the synchronous callback function instead.

Note that we pass in all the props in this component to the ContactForm component. To pass an argument a onClick handler function, we have to call bind on the function and pass in the argument for the function as a second argument to bind . For example, in this file, we have editContact.bind(this, c) , where c is the contact object. The editContact function is defined as follows:

const editContact = (contact) => {
  setSelectedContact(contact);
  setOpenEditModal(true);
}

c is the contact parameter we pass in.

Next we create a file called HomePage.css and put:

.home-page {
  padding: 20px;
}

to add some padding.

In index.js , we replace the existing code with:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { contactsReducer } from './reducers';
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'

const addressBookApp = combineReducers({
    contacts: contactsReducer,
})

const store = createStore(addressBookApp)

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

We combined the reducers and create the store, then inject it to our app with the Provider component so that we can use it everywhere in the app.

Then we make a file called reducers.js , and add:

import { SET_CONTACTS } from './actions';

function contactsReducer(state = {}, action) {
    switch (action.type) {
        case SET_CONTACTS:
            state = JSON.parse(JSON.stringify(action.payload));
            return state;
        default:
            return state
    }
}

export { contactsReducer };

This is the reducer where we store the contacts that we dispatch by calling the prop provided by the mapDispatchToProps function in our components.

Then we make a file called requests.js , and add:

const APIURL = 'http://localhost:3000';
const axios = require('axios');
export const getContacts = () => axios.get(`${APIURL}/contacts`);
export const addContact = (data) => axios.post(`${APIURL}/contacts`, data);
export const editContact = (data) => axios.put(`${APIURL}/contacts/${data.id}`, data);
export const deleteContact = (id) => axios.delete(`${APIURL}/contacts/${id}`);

These are functions are making our HTTP requests to the back end to save and delete contacts.

Finally, in public/index.html , we replace the existing code with:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="apple-touch-icon" href="logo192.png" />
  <link rel="manifest" crossorigin="use-credentials" href="%PUBLIC_URL%/manifest.json" />

<!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
  <title>React Address Book App</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
  <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
</body>

</html>

to change the title and add the Bootstrap stylesheet.

Now we can run the app by running set PORT=3001 && react-scripts start on Windows or PORT=3006 react-scripts start on Linux.

To start back end, we first install the json-server package by running npm i json-server . Them go to our project folder and run:

json-server --watch db.json

In db.json , change the text to:

{
  "contacts": [
  ]
}

so that we have the contacts endpoints defined in requests.js available.

Categories
Tools

Useful Linux Commands — Text and Arguments

Linux is an operating system that many developers will use.

Therefore, it’s a good idea to learn some Linux commands.

In this article, we’ll look at some useful Linux commands we should know.

xargs

The xargs command lets us pass output of a command and use it as an argument to another command.

The general syntax is:

command1 | xargs command2

For instance, we can write:

cat deleteall.txt | xargs rm

to the file paths in deleteall.txt to the rm command to delete all the files listed in deleteall.txt .

With the -p switch, we see a confirmation prompt before the command is run:

cat deleteall.txt | xargs -p rm

We can run multiple commands with the -I switch:

command1 | xargs -I % /bin/bash -c 'command2 %; command3 %'

The commands are the ones after the % .

df

The df command lets us get disk usage information.

We can use the -h switch to get the values in a human-readable format.

nohup

The nohup command lets us run a command that doesn’t end when the terminal is killed.

We just put the command after nohup to run it.

diff

diff lets us compare the content of 2 text files.

For example, we run:

diff dogs.txt dogs2.txt

to compare the content of dogs.txt and dogs2.txt .

The -y switch will make diff compare the 2 files line by line.

We can also use it to compare directories with the -r switch:

diff -r dir1 dir2

We can compare which files differ with the r and q switches together:

diff -rq dir1 dir2

uniq

The uniq command lets us get unique lines of text from a file.

For instance, we run:”

uniq dogs.txt

to get the unique lines of text from the dogs.txt file.

We can also use it to get unique lines of output from a command with the | operator:

ls | uniq

We can sort the lines before getting the unique lines with the sort command:

sort dogs.txt | uniq

We can display only the duplicate lines with the -d switch:

sort dogs.txt | uniq -d

The -u switch makes it only display the unique lines:

sort dogs.txt | uniq -u

The -c switch lets us count the occurrences of each line:

sort dogs.txt | uniq -c

sort

The sort command lets us sort lines of text or text command outputs.

For example, we run:

sort dogs.txt

to sort the lines in dogs.txt .

We can use the -r switch to sort the items in reverse order:

sort -r dogs.txt

And we can use the -u switch to return only the unique lines:

sort -u dogs.txt

We can also use sort to sort command outputs.

For instance, we can sort the output from the ls command with:

ls | sort

Conclusion

We can pass arguments to commands, and diff and manipulate text outputs with various Linux commands.

Categories
Tools

Useful Linux Commands — Environment Variables and Cron Jobs

Linux is an operating system that many developers will use.

Therefore, it’s a good idea to learn some Linux commands.

In this article, we’ll look at some useful Linux commands we should know.

env

The env command lets us set environment variables for the current session.

For instance, we can run:

env USER=username node app.js

to set the USER environment variable in the current session.

We can clear environment variables with the -i switch:

env -i node app.js

We can use the -u switch to make a variable inaccessible:

env -u USER node app.js

Now the USER environment variable won’t be visible to programs in the session.

printenv

The printenv command lets us print environment variables onto the screen.

We can print a specific one by specifying it after the command.

For instance, we run:

printenv USER

to print the value of the USER environment variable.

basename

The basename command lets us return the file name portion of the path.

For instance, we run:

basename /users/foo/test.txt

to print test.txt onto the screen.

If the last segment is a directory, then we get that directory’s name.

dirname

The c command lets us get the direction portion of a path.

For instance, if we have:

dirname /users/foo/test.txt

Then we get /users/foo displayed on the screen.

crontab

The crontab command lets us schedule cron jobs.

We can list the corn jobs scheduled with the -l switch:

crontab -l

We can run:

crontab -e

to add new cron jobs.

The syntax for setting the schedule for a cron job is the following.

To set the minute that the cron job runs, we can add one or more of the following:

  • * — any value
  • , — value list separator
  • - — range of values
  • / — step values

To set the hour that the cron job runs, we can add one or more of the following:

  • * — any value
  • , — value list separator
  • - — range of values
  • / — step values
  • 0–23

To set the day of the month that the cron job runs, we can add one or more of the following:

  • * — any value
  • , — value list separator
  • - — range of values
  • / — step values
  • 1–31

To set the month that the cron job runs, we can add one of the following:

  • * — any value
  • , — value list separator
  • - — range of values
  • / — step values
  • 1–12

To set the day of the week that the cron job runs, we can add one of the following:

  • * — any value
  • , — value list separator
  • - — range of values
  • / — step values
  • 0–6 — (0 is Sunday to 6 is Saturday)
  • SUN-SAT

For instance, we can run:

30 08 10 06 * /home/ramesh/full-backup

to set the full-backup script to run at a specified time.

The expression means:

  • 30–30th Minute
  • 08–08 AM
  • 10–10th Day
  • 06–6th Month (June)
  • ***** — Every day of the week

Conclusion

We can use Linux commands to get and set environment variables and view cron jobs.

Categories
Tools

Useful Linux Commands — Network and Compression

Linux is an operating system that many developers will use.

Therefore, it’s a good idea to learn some Linux commands.

In this article, we’ll look at some useful Linux commands we should know.

export

The export command let us export variables to child processes.

For instance, we run:

export TEST="test"

to set the TEST variable to 'test' .

We can also reference another variable by using the $ in the expression on the right side.

For example, we run:

export PATH=$PATH:/new/path

to reference the existing value of the PATH environment variable on the right side.

tar

We can run the tar command to archive files.

To create a tar archive, we run:

tar -cf archive.tar file1 file2

to create a tar archive with the file1 and file2 files in the tar archive.

We can extract files from an archive in the current folder by running:

tar -xf archive.tar

And we can extract an archive into a specific directory with:

tar -xf archive.tar -C dir

x stands for extract and f means write files.

c means create.

The z option lets us create a gzip archive.

For instance, we run:

tar -czf archive.tar.gz file1 file2

to create a gzip archive with file1 and file2 inside.

And we can gunzip a gzip archive with:

tar -xf archive.tar.gz

traceroute

The traceroute command lists all the nodes traversed to reach a host.

For instance, we run:

traceroute google.com

to see where the packets go to reach Google.

ping

The ping command lets us ping a network host to see if it responds.

For instance, we run:

ping google.com

to see if Google is up.

We can continually ping it with the -c switch:

ping -c google.com

It stops pinging only when we press ctrl+c.

gunzip

The gunzip command lets us unzip a zip archive.

For instance, we can run:

gunzip filename.gz

to extract the files in the filename.gz file.

We can use the -c switch extract a file to a different filename:

gunzip -c filename.gz > bar

Then we extract filename.gz to the bar file.

gzip

The gzip command lets us create a compressed file.

For instance, we run:

gzip filename

to compress the filename file.

We can rename the gzipped file with the -c switch:

gzip -c filename > filename.gz

Then we compress the file into the filename.gz file.

The -k option also lets us change the name of the compressed file:

gzip -k filename

We can set the compression level with the -<number> switch.

For instance, we run:

gzip -1 filename

to compress filename with level 1 compression.

We can compress multiple file by adding the file names separated with spaces:

gzip file1 file2

We compress file1 and file2 with one gzip command.

Also, we can compress all files in a directory recursively with the -r switch:

gzip -r folder

The -v switch prints the compression percentage info.

The -d switch decompresses a file:

gzip -d filename.gz

Conclusion

We can compress files and folders, and do basic network tasks with some basic Linux commands.

Categories
jQuery

jQuery — Key Events and Select Element by Type

jQuery is a popular JavaScript for creating dynamic web pages.

In this article, we’ll look at how to using jQuery in our web apps.

.keypress()

We can use the keypress method to listen to keypress events or trigger it.

For example, if we have:

<form>
  <fieldset>
    <input id="target" type="text" value="Hello there">
  </fieldset>
</form>
<div id="other">
  Trigger the handler
</div>

Then we can listen to the keypress event on the input by writing:

$("#target").keypress(function() {
  console.log("Handler for .keypress() called.");
});

We can also trigger the keypress event when we click on the div with ID other by writing:

$("#target").keypress(function() {
  console.log("Handler for .keypress() called.");
});$("#other").click(function() {
  $("#target").keypress();
});

.keyup()

The .keyup() method lets us listen to the keyup event or trigger it.

For instance, if we have:

<form>
  <fieldset>
    <input id="target" type="text" value="Hello there">
  </fieldset>
</form>
<div id="other">
  Trigger the handler
</div>

Then we can listen to the keyup event on the input by writing:

$("#target").keyup(function() {
  console.log("Handler for .keypress() called.");
});

We can also trigger the keyup event when we click on the div with ID other by writing:

$("#target").keyup(function() {
  console.log("Handler for .keypress() called.");
});$("#other").click(function() {
  $("#target").keyup();
});

:lang() Selector

The :lang selector lets us select all elements of the specified language.

For example, if we have the following HTML:

<div lang="en-us">red
  <div>white
    <div>and blue</div>
  </div>
</div>
<div lang="es-es">rojo
  <div>amarillo
    <div>y rojo</div>
  </div>
</div>

And CSS:

.spain {
  background-color: red;
  color: #ff0;
}.usa {
  background-color: red;
  color: #fff;
}

Then we can add the different classes to the div with different lang attribute values by writing:

$("div:lang(en-us)").addClass("usa");
$("div:lang(es-es)").addClass("spain");

.last()

We can get the last element in a set of matched elements with the .last method.

For example, if we have:

<ul>
  <li>list item 1</li>
  <li>list item 2</li>
  <li>list item 3</li>
  <li>list item 4</li>
  <li>list item 5</li>
</ul>

Then we add a red background to the last li by writing:

$("li").last().css("background-color", "red");

We get the li's and then call last to get the last one.

:last-child Selector

The :last-child selector selects all elements that are the last child of the parent.

For example, if we have the following HTML:

<div>
  <span>John,</span>
  <span>Karl,</span>
  <span>Brandon,</span>
  <span>Sam</span>
</div>
<div>
  <span>James,</span>
  <span>Mary,</span>
  <span>Ralph,</span>
  <span>David</span>
</div>

and CSS:

span.solast {
  text-decoration: line-through;
}

Then we can make the last span of each div red and add a strikethrough when web hover over them by writing:

$("div span:last-child")
  .css({
    color: "red",
    fontSize: "80%"
  })
  .hover(function() {
    $(this).addClass("solast");
  }, function() {
    $(this).removeClass("solast");
  });

:last-of-type Selector

We can get the last element of a given type with the :last-pf-type selector.

For example, if we have the following HTML:

<div>
  <span>Corey,</span>
  <span>Yehuda,</span>
  <span>Adam,</span>
  <span>Todd</span>
</div>
<div>
  <span>James,</span>
  <span>Mary,</span>
  <span>Tim,</span>
  <b>Nobody</b>
</div>

and CSS:

span.solast {
  text-decoration: line-through;
}

Then we can make the last span of each div red and add a strikethrough when web hover over them by writing:

$("span:last-of-type")
  .css({
    color: "red",
    fontSize: "80%"
  })
  .hover(function() {
    $(this).addClass("solast");
  }, function() {
    $(this).removeClass("solast");
  });

Conclusion

We can listen to key events and get elements by type with jQuery.