Categories
React Answers

How to Embed a YouTube Video into a React App?

To embed a YouTube video into a React app, we can add an iframe into a React component with the embed video URL as the value of the src prop.

For instance, we write:

import React from "react";

export default function App() {
  return (
    <div>
      <iframe
        src="https://www.youtube.com/embed/C0DPdy98e4c"
        frameborder="0"
        allow="autoplay; encrypted-media"
        allowfullscreen
        title="video"
      />{" "}
    </div>
  );
}

We set src to the URL of the video we want to display.

frameborder is set to 0 to remove the iframe’s border.

allowfullscreen lets the user make the video full screen.

Categories
React

How to Add Copy to Clipboard Feature to Your React App

Copy to clipboard feature is a popular convenience feature for web apps like password managers, where it is inconvenient for people to highlight text and then copy it. It is an easy feature to add to your own web app.

In this article, we will build a password manager that lets you enter, edit and delete password to the websites the user goes to and let them copy their username and password to the clip to use them anywhere they like. We will use React to build the app.

To start, we will run Create React App to create the app. Run:

npx create-react-app password-manager

to create the app. Next, we add our own libraries, we will use Axios for making HTTP requests to our back end, Formik and Yup for form value handling and form validation respectively, MobX for state management, React Bootstrap for styling, React-Copy-To-Clipboard for letting us copy data to the clipboard, and React Router for routing.

We install them by running:

npm i axios formik mobx mobx-react react-bootstrap react-copy-to-clipboard react-router-dom yup

With all the libraries installed, we can start building our app. We create all the files in the src folder unless otherwise specified.

First, we replace the existing code in App.css with:

.bg-primary {
  background-color: #09d3ac !important;
}

to change the top bar’s background color. Next in App.js , replace the current code with:

import React from "react";
import { Router, Route } 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({ passwordsStore }) {
  return (
    <div className="App">
      <Router history={history}>
        <Navbar bg="primary" expand="lg" variant="dark">
          <Navbar.Brand href="#home">Password Manager</Navbar.Brand>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Nav className="mr-auto">
              <Nav.Link href="/" active>Home</Nav.Link>
            </Nav>
          </Navbar.Collapse>
        </Navbar>
        <Route
          path="/"
          exact
          component={props => (
            <HomePage {...props} passwordsStore={passwordsStore} />
          )}
        />
      </Router>
    </div>
  );
}

export default App;

to add our React Bootstrap top bar and our route to the home page. passwordStore is our MobX store for storing our password list in the front end.

Next, create HomePage.css and add:

.home-page {
  padding: 20px;
}

to add some padding to our page.

Then create HomePage.js and add:

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 PasswordForm from "./PasswordForm";
import "./HomePage.css";
import { deletePassword, getPasswords } from "./requests";
import { observer } from "mobx-react";
import { CopyToClipboard } from "react-copy-to-clipboard";

function HomePage({ passwordsStore }) {
  const [openAddModal, setOpenAddModal] = useState(false);
  const [openEditModal, setOpenEditModal] = useState(false);
  const [initialized, setInitialized] = useState(false);
  const [selectedPassword, setSelectedPassword] = useState({});

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

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

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

  const editPassword = contact => {
    setSelectedPassword(contact);
    setOpenEditModal(true);
  };

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

  const getData = async () => {
    const response = await getPasswords();
    passwordsStore.setPasswords(response.data);
    setInitialized(true);
  };

  const deleteSelectedPassword = async id => {
    await deletePassword(id);
    getData();
  };

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

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

      <Modal show={openEditModal} onHide={closeModal}>
        <Modal.Header closeButton>
          <Modal.Title>Edit Password</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <PasswordForm
            edit={true}
            onSave={closeModal.bind(this)}
            contact={selectedPassword}
            onCancelEdit={cancelEditModal}
            passwordsStore={passwordsStore}
          />
        </Modal.Body>
      </Modal>
      <ButtonToolbar onClick={openModal}>
        <Button variant="outline-primary">Add Password</Button>
      </ButtonToolbar>
      <br />
      <div className="table-responsive">
        <Table striped bordered hover>
          <thead>
            <tr>
              <th>Name</th>
              <th>URL</th>
              <th>Username</th>
              <th>Password</th>
              <th></th>
              <th></th>
              <th></th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            {passwordsStore.passwords.map(c => (
              <tr key={c.id}>
                <td>{c.name}</td>
                <td>{c.url}</td>
                <td>{c.username}</td>
                <td>******</td>
                <td>
                  <CopyToClipboard text={c.username}>
                    <Button variant="outline-primary">
                      Copy Username to Clipboard
                    </Button>
                  </CopyToClipboard>
                </td>
                <td>
                  <CopyToClipboard text={c.password}>
                    <Button variant="outline-primary">
                      Copy Password to Clipboard
                    </Button>
                  </CopyToClipboard>
                </td>
                <td>
                  <Button
                    variant="outline-primary"
                    onClick={editPassword.bind(this, c)}
                  >
                    Edit
                  </Button>
                </td>
                <td>
                  <Button
                    variant="outline-primary"
                    onClick={deleteSelectedPassword.bind(this, c.id)}
                  >
                    Delete
                  </Button>
                </td>
              </tr>
            ))}
          </tbody>
        </Table>
      </div>
    </div>
  );
}
export default observer(HomePage);

This component is the home page of our app. We have a table to display the list of passwords, a button to add a login and password entry, and buttons in each row of the table to copy username and password, and edit and delete each entry. We have the name, URL, username and password columns. The CopyToClipboard component allows us to copy the data we copy to the text prop of the component. Any component can be inside this component. We have one React Bootstrap modal for add a password and another one for edit. PasswordForm is our form for adding the password entries, which we will create later.

We have the openModal , closeModal , cancelAddModal , and cancelEditModal functions to open and close the modals. In the editPassword function, we call the setSelectedPassword function to set the password entry to be edited.

The observer we wrap around the HomePage component is for letting us watch the latest values from passwordsStore .

Next, we modify index.js to have:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { PasswordsStore } from "./store";
const passwordsStore = new PasswordsStore();

ReactDOM.render(
  <App passwordsStore={passwordsStore} />,
  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](https://bit.ly/CRA-PWA)
serviceWorker.unregister();

We pass in our PasswordStore MobX store here, which will pass it to all the other components.

Next, we create PasswordForm.js and add:

import React from "react";
import { Formik } from "formik";
import Form from "react-bootstrap/Form";
import Col from "react-bootstrap/Col";
import Button from "react-bootstrap/Button";
import * as yup from "yup";
import PropTypes from "prop-types";
import { addPassword, getPasswords, editPassword } from "./requests";

const schema = yup.object({
  name: yup.string().required("Name is required"),
  url: yup
    .string()
    .url()
    .required("URL is required"),
  username: yup.string().required("Username is required"),
  password: yup.string().required("Password is required")
});

function PasswordForm({
  edit,
  onSave,
  contact,
  onCancelAdd,
  onCancelEdit,
  passwordsStore
}) {
  const handleSubmit = async evt => {
    const isValid = await schema.validate(evt);
    if (!isValid) {
      return;
    }
    if (!edit) {
      await addPassword(evt);
    } else {
      await editPassword(evt);
    }
    const response = await getPasswords();
    passwordsStore.setPasswords(response.data);
    onSave();
  };

  return (
    <>
      <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="name">
                <Form.Label>Name</Form.Label>
                <Form.Control
                  type="text"
                  name="name"
                  placeholder="Name"
                  value={values.name || ""}
                  onChange={handleChange}
                  isInvalid={touched.name && errors.name}
                />
                <Form.Control.Feedback type="invalid">
                  {errors.name}
                </Form.Control.Feedback>
              </Form.Group>

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

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

              <Form.Group as={Col} md="12" controlId="password">
                <Form.Label>Password</Form.Label>
                <Form.Control
                  type="password"
                  name="password"
                  placeholder="Password"
                  value={values.password || ""}
                  onChange={handleChange}
                  isInvalid={touched.password && errors.password}
                />
                <Form.Control.Feedback type="invalid">
                  {errors.password}
                </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>
    </>
  );
}

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

export default PasswordForm;

Here, we add our form for letting users enter the username and password of their websites. We use the Yup schema object we created at the top of our code to make sure all fields are entered and check that the URL entered is actually a URL. We use the Formik component to handle the form of input changes and get the latest values.

Once the form is checked to be valid by schema.validate promise resolving to true , then addPassword or editPassword functions from requests.js , which we will create later will be called depending if the user is adding or editing an entry. Once that succeeds, then the getPasswords from the same file is called, and then setPasswords from passwordsStore is called to store the passwords in the store. Finally, onSave passed in from the props in HomePage component is called to close the modal.

Next create requests.js and add:

const APIURL = 'http://localhost:3000';
const axios = require('axios');

export const getPasswords = () => axios.get(`${APIURL}/passwords`);

export const addPassword = (data) => axios.post(`${APIURL}/passwords`, data);

export const editPassword = (data) => axios.put(`${APIURL}/passwords/${data.id}`, data);

export const deletePassword = (id) => axios.delete(`${APIURL}/passwords/${id}`);

to let us make the requests to our back end to save the password entries.

Then we create our MobX store by creating store.js and add:

import { observable, action, decorate } from "mobx";

class PasswordsStore {
  passwords = [];

  setPasswords(passwords) {
    this.passwords = passwords;
  }
}

PasswordsStore = decorate(PasswordsStore, {
  passwords: observable,
  setPasswords: action
});

export { PasswordsStore };

We have the passwords field which can be observed for the latest value if we wrap the observer function provided by MobX outside a component. The setPasswords is used to set the latest password entries in the store so that they can be propagated to the components.

Finally, in 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" />
    <!--
      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/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      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>Password Manager</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 CSS.

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 the back end, we first install the json-server package by running npm i json-server. Then, go to our project folder and run:

json-server --watch db.json

In db.json, change the text to:

{
  "passwords": [
  ]
}

So we have the passwords endpoints defined in the requests.js available.

Categories
React Answers

How to Dynamically Load a Stylesheet with React?

Sometimes, we want to dynamically load a stylesheet with React.

In this article, we’ll look at how to dynamically load a stylesheet with React.

Dynamically Load a Stylesheet with React

To dynamically load a stylesheet with React, we can add a link element with the attributes we want.

For instance, we write:

import React, { useState } from "react";

export default function App() {
  const [stylePath] = useState(
    "https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css"
  );

  return (
    <div>
      <link rel="stylesheet" type="text/css" href={stylePath} />
    </div>
  );
}

to create the stylePath state with the useState hook.

Its initial value is set to the URL of the stylesheet that we want to include.

Then we add a link element with the href prop set to stylePath to add the stylesheet at the given URL into the component.

Conclusion

To dynamically load a stylesheet with React, we can add a link element with the attributes we want.

Categories
React Answers

How to Map Only a Portion of an Array to Components in a React Component?

Sometimes, we want to map only a portion of an array to components in a React component.

In this article, we’ll look at how to map only a portion of an array to components in a React component.

Map Only a Portion of an Array to Components in a React Component

To map only a portion of an array to components in a React component, we can use the JavaScript array’s filter method to return an array of the items we want to map before calling map.

For instance, we write:

import React from "react";

export default function App() {
  const feed = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];

  return (
    <div>
      {feed
        .filter((item) => item <= 5)
        .map((filteredItem) => (
          <p key={filteredItem}>{filteredItem}</p>
        ))}
    </div>
  );
}

to create the feed array.

And we want to display the first 5 entries from feed.

To do this, we call filter with (item) => item <= 5 to return the first 5 elements.

Then we call map with a callback to return p elements with the content of the elements to display them on the screen.

Now we see:

1

2

3

4

5

on the screen

Conclusion

To map only a portion of an array to components in a React component, we can use the JavaScript array’s filter method to return an array of the items we want to map before calling map.

Categories
React Answers

How to Turn an SVG String into an Image in a React Component?

Sometimes, we want to turn an SVG string into an image in a React component.

In this article, we’ll look at how to turn an SVG string into an image in a React component.

Turn an SVG String into an Image in a React Component

To turn an SVG string into an image in a React component, we can put the SVG string in a base64 URL string.

Then we can use the base64 URL string as the URL of the image.

For instance, we write:

import React from "react";

export default function App() {
  const image =
    '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" width="47.4" height="40.65" viewBox="21 18.5 158 135.5"><path d="M25,50 l150,0 0,100 -150,0 z" stroke-width="4" stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke="black" fill="black" ></path><g transform="translate(0,0)" stroke-width="4" stroke="black" fill="none" ><circle cx="100" cy="30" r="7.5" fill="black" ></circle><circle cx="70" cy="30" r="7.5" fill="black" ></circle><circle cx="130" cy="30" r="7.5" fill="black" ></circle></g></svg>';

  return (
    <div>
      <img src={`data:image/svg+xml;utf8,${image}`} />
    </div>
  );
}

to assign the image variable to a SVG string.

Then we set the src prop to a base64 URL that has the image string in it.

Now we should see the SVG displayed on the screen.

Conclusion

To turn an SVG string into an image in a React component, we can put the SVG string in a base64 URL string.

Then we can use the base64 URL string as the URL of the image.