Categories
React

How to Add Geolocation to a React App

Many apps want to get data based on location. This is where the HTML Geolocation API comes in. You can use it easily to get the location of the current device via the Internet.

To get the location of the device the browser is running with plain JavaScript, we write:

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(getPosition);
}

function getPosition(position) {
  console.log(position.coords.latitude, position.coords.longitude);
}

As you can see, getting the latitude and longitude is very easy. We can also easily add geolocation to any React app. The react-geolocated package is a great add-on for adding geolocation capabilities to your app. It is located at https://www.npmjs.com/package/react-geolocated.

It provides a promise-based API for getting the location of the device, so we can easily use async and await to get the location with this package.

In this article, we will build an app to weather app that lets you get the current weather and forecast of the current location you’re in. It also lets you search the weather by inputting the location manually.

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

npx create-react-app weather-app

to create the project.

Next, we have to add some libraries. We need Axios for making HTTP requests, Formik and Yup for form value handling and validation, MobX for state management, Bootstrap for styling, React Geolocated for getting geolocation data, and React Router for routing. We install them by running:

npm i axios formik mobx mobx-react react-bootstrap react-geolocated react-router-dom yup

With all the packages installed, we can start building the app. We start by replacing the code in App.css :

.page {
  padding: 20px;
}

to add some padding to our page.

Next, replace the code in App.js 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({ keywordStore }) {
  return (
    <div className="App">
      <Router history={history}>
        <Navbar bg="primary" expand="lg" variant="dark">
          <Navbar.Brand href="#home">Geolocation Weather App</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} keywordStore={keywordStore} />
          )}
        />
      </Router>
    </div>
  );
}

export default App;

Here we add the React Bootstrap navigation bar and the route to our home page.

Next, we create a component to display the current weather. Create a file called CurrentWeather.js in the src folder and add:

import React from "react";
import { observer } from "mobx-react";
import { searchWeather } from "./request";
import ListGroup from "react-bootstrap/ListGroup";

function CurrentWeather({ keywordStore }) {
  const [weather, setWeather] = React.useState({});

  const getWeatherForecast = async keyword => {
    const response = await searchWeather(keyword);
    setWeather(response.data);
  };

  React.useEffect(() => {
    keywordStore.keyword && getWeatherForecast(keywordStore.keyword);
  }, [keywordStore.keyword]);

  return (
    <div>
      {weather.main ? (
        <ListGroup>
          <ListGroup.Item>
            Current Temparature: {weather.main.temp - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>
            High: {weather.main.temp_max - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>
            Low: {weather.main.temp_min - 273.15} C
          </ListGroup.Item>
          <ListGroup.Item>Pressure: {weather.main.pressure} </ListGroup.Item>
          <ListGroup.Item>Humidity: {weather.main.humidity}</ListGroup.Item>
        </ListGroup>
      ) : null}
    </div>
  );
}
export default observer(CurrentWeather);

We get the search keyword from the keywordStore which we will build and search for the current weather when keywordStore.keyword is updated by passing it in the array of the second argument of the React.useEffect function.

Once the data is retrieved, we set the data with the setWeather function and display the data in a ListGroup at the bottom of the page.

We wrap observer around our component in the last line so that we get the latest keyword value from keywordStore .

Next, we add a component for display the forecast, add Forecast.js in the src folder and add:

import React from "react";
import { observer } from "mobx-react";
import { searchForecast } from "./request";
import ListGroup from "react-bootstrap/ListGroup";
import Card from "react-bootstrap/Card";

function Forecast({ keywordStore }) {
  const [forecast, setForecast] = React.useState({});

  const getWeatherForecast = async keyword => {
    const response = await searchForecast(keyword);
    setForecast(response.data);
  };

  React.useEffect(() => {
    keywordStore.keyword && getWeatherForecast(keywordStore.keyword);
  }, [keywordStore.keyword]);

  return (
    <div>
      {Array.isArray(forecast.list) ? (
        <div>
          {forecast.list.map(l => {
            return (
              <Card body>
                <ListGroup>
                  <ListGroup.Item>Date: {l.dt_txt}</ListGroup.Item>
                  <ListGroup.Item>
                    Temperature: {l.main.temp - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>
                    High: {l.main.temp_max - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>
                    Low: {l.main.temp_min - 273.15} C
                  </ListGroup.Item>
                  <ListGroup.Item>Pressure: {l.main.pressure} C</ListGroup.Item>
                </ListGroup>
              </Card>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}
export default observer(Forecast);

It’s very similar to CurrentWeather.js except that the data is in a list. So we map forecast.list into an array of Card s instead of just rendering it.

Next, we create a HomePage.js file in the src folder 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 { observer } from "mobx-react";
import * as yup from "yup";
import Tabs from "react-bootstrap/Tabs";
import Tab from "react-bootstrap/Tab";
import CurrentWeather from "./CurrentWeather";
import Forecast from "./Forecast";
import { geolocated } from "react-geolocated";
import { getLocationByLatLng } from "./request";

const schema = yup.object({
  keyword: yup.string().required("Keyword is required")
});

const buttonStyle = { marginRight: "10px" };

function HomePage({ keywordStore, coords }) {
  const [initialized, setInitialized] = React.useState(false);

  const handleSubmit = async evt => {
    const isValid = await schema.validate(evt);
    if (!isValid) {
      return;
    }
    localStorage.setItem("keyword", evt.keyword);
    keywordStore.setKeyword(evt.keyword);
  };

  const getWeatherCurrentLocation = async () => {
    const { latitude, longitude } = coords;
    const { data } = await getLocationByLatLng(latitude, longitude);
    const keyword = (
      data.results[0].address_components.find(c =>
        c.types.includes("locality")
      ) || {}
    ).long_name;
    localStorage.setItem("keyword", keyword);
    keywordStore.setKeyword(keyword);
  };

  const clear = () => {
    localStorage.clear();
    keywordStore.setKeyword("");
  };

  React.useEffect(() => {
    if (!initialized) {
      keywordStore.setKeyword(localStorage.getItem("keyword") || "");
      setInitialized(true);
    }
  }, [keywordStore.keyword]);

  return (
    <div className="page">
      <h1>Weather</h1>
      <Formik
        validationSchema={schema}
        onSubmit={handleSubmit}
        initialValues={{ keyword: localStorage.getItem("keyword") || "" }}
        enableReinitialize={true}
      >
        {({
          handleSubmit,
          handleChange,
          handleBlur,
          values,
          touched,
          isInvalid,
          errors
        }) => (
          <Form noValidate onSubmit={handleSubmit}>
            <Form.Row>
              <Form.Group as={Col} md="12" controlId="keyword">
                <Form.Label>City</Form.Label>
                <Form.Control
                  type="text"
                  name="keyword"
                  placeholder="City"
                  value={values.keyword || ""}
                  onChange={handleChange}
                  isInvalid={touched.keyword && errors.keyword}
                />
                <Form.Control.Feedback type="invalid">
                  {errors.keyword}
                </Form.Control.Feedback>
              </Form.Group>
            </Form.Row>
            <Button type="submit" style={buttonStyle}>
              Search
            </Button>
            <Button
              type="button"
              onClick={getWeatherCurrentLocation}
              style={buttonStyle}
              disabled={!coords}
            >
              Get Weather of Current Location
            </Button>
            <Button type="button" onClick={clear}>
              Clear
            </Button>
          </Form>
        )}
      </Formik>
      <br />
      <Tabs defaultActiveKey="weather">
        <Tab eventKey="weather" title="Current Weather">
          <CurrentWeather keywordStore={keywordStore} />
        </Tab>
        <Tab eventKey="forecast" title="Forecast">
          <Forecast keywordStore={keywordStore} />
        </Tab>
      </Tabs>
    </div>
  );
}

HomePage = observer(HomePage);

export default geolocated({
  positionOptions: {
    enableHighAccuracy: false
  },
  userDecisionTimeout: 5000
})(HomePage);

We use the React-Geolocated package here to add the geolocation functionality to our app. The package provides us with the geolocated higher-order component to let users get their current location if they allow our app to do so. We set enableHighAccuracy to false so that our app will respond faster by using a less accurate location, which is already accurate in most cases. userDecisionTimeout is set so that after 5 seconds, geolocation will be assumed to be disabled by the user.

Since we wrapped our HomePage component with the geolocated component, we will get the coords prop so that we get the location when geolocation is enabled. We add check for the coords prop in our ‘Get Weather of Current Location‘ button so that users can only use the geolocation functionality when they have geolocation enabled.

In the callback for the useEffect hook, we set the array in the second argument to watch for keywordStore.keyword changes so that it will re-render when that changes. We need this so that we get the latest keyword into our form via the intialValues prop of our Formik component. When we click the ‘Get Weather of Current Location‘ button, we get set the keyword in both local storage and the MobX store, so we can just watch the store to make it render the local storage’s keyword value.

In the getWeatherCurrentLocation function, we get the location with the Google Maps API, then get the city name by finding the component with locality in the types property, and get the long_name from that, then set the keyword in our MobX store for use by the previous components we created and also in local storage so that we see it in our form and keep the same value set after refreshing the value.

Next in index.js 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 { KeywordStore } from "./store";
const keywordStore = new KeywordStore();

ReactDOM.render(
  <App keywordStore={keywordStore} />,
  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();

to pass or MobX store into our app.

Then in we create requests.js in the src folder, and add the following:

const APIURL = "http://api.openweathermap.org";
const GOOGLE_MAP_API_URL = "https://maps.googleapis.com/maps/api/geocode/json";
const axios = require("axios");

export const searchWeather = loc =>
  axios.get(
    `${APIURL}/data/2.5/weather?q=${loc}&appid=${process.env.REACT_APP_APIKEY}`
  );

export const searchForecast = loc =>
  axios.get(
    `${APIURL}/data/2.5/forecast?q=${loc}&appid=${process.env.REACT_APP_APIKEY}`
  );

export const getLocationByLatLng = (lat, lng) =>
  axios.get(
    `${GOOGLE_MAP_API_URL}?latlng=${lat},${lng}&key=${process.env.REACT_APP_GOOGLE_APIKEY}`
  );

We have functions to make HTTP requests to get the weather and location with the Google API. To get the properties in process.env , put them in your .env file in your project’s root folder with those field properties as keys and the corresponding API keys as the values.

After that, create store.js in the src folder and add:

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

class KeywordStore {
  keyword = "";

  setKeyword(keyword) {
    this.keyword = keyword;
  }
}

KeywordStore = decorate(KeywordStore, {
  keyword: observable,
  setKeyword: action
});

export { KeywordStore };

to create our MobX Store. We have the keyword field that is watched by other components by wrappingobserver function outside our component when we export them. Also, we have the setKeyword function to set the latest values of the keyword field.

Finally, in index.js , 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>Geolocation Weather 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 Bootstrap CSS.

Categories
React

Add a Modal to a React App with react-modal-video and react-responsive-modal

Modals are something that we have to add often into our React app.

To make this task easier, we can use existing component libraries to add them.

In this article, we’ll look at how to add a modal into our React app with the react-modal-video and react-responsive-modal libraries.

react-modal-video

The react-modal-video library lets us add a video modal into our React app.

To install it, we run:

npm i react-modal-video

Then we can use it by writing:

import React, { useState } from "react";
import ModalVideo from "react-modal-video";
import "react-modal-video/scss/modal-video.scss";

export default function App() {
  const [isOpen, setOpen] = useState(false);

  return (
    <React.Fragment>
      <ModalVideo
        channel="youtube"
        autoplay
        isOpen={isOpen}
        videoId="sSZNLAIL65M"
        onClose={() => setOpen(false)}
      />

      <button className="btn-primary" onClick={() => setOpen(true)}>
        open
      </button>
    </React.Fragment>
  );
}

We add the ModalVideo component to add the video modal.

The channel prop lets us set the site with the video.

autoplay enables autoplay.

isOpen has the modal open state.

videoId has the ID for the video to embed in the modal.

onClose is run when we close the modal.

Other sites supported include Vimeo and Yorku.

We can set the aspect ratio with the ratio prop.

allowFullScreen lets us enable full-screen display.

animationSpeed sets the speed of the animation when we open or close the modal.

We can also set the class names of various parts of the modal with the modalVideo , modalVideoClose , modalVideoBody , modalVideoInner , and other props.

react-responsive-modal

The react-responsive-modal library is another library that lets us add modals into our React app.

To install it, we run:

npm i react-responsive-modal

to install it with NPM.

We can also install it with Yarn by running:

yarn add react-responsive-modal

Then we can use it by writing:

import React, { useState } from "react";
import "react-responsive-modal/styles.css";
import { Modal } from "react-responsive-modal";

export default function App() {
  const [open, setOpen] = useState(false);
  const onOpenModal = () => setOpen(true);
  const onCloseModal = () => setOpen(false);

  return (
    <div>
      <button onClick={onOpenModal}>Open modal</button>
      <Modal open={open} onClose={onCloseModal} center>
        <h2>Simple centered modal</h2>
      </Modal>
    </div>
  );
}

We import the CSS file with the modal styles.

And we have the open state to open the modal when it’s true and close it otherwise.

Then we add the modal with the Modal component.

We pass in the open state as the value of the open prop to control it.

And we set open to false when onClose is run, which is when we close the modal.

center centers the modal in the screen.

The modal content is the children of the Modal component.

Conclusion

The react-modal-video and react-responsive-modal libraries let us add modals easily into our React app.

Categories
React

react-overlays — Portals and Click Outside

Overlays are something that we have to add often into our React app.

To make this task easier, we can use existing component libraries to add them.

In this article, we’ll look at how to add portals and click outside features into our React app with the react-overlays library.

Portal

We can use the Portal component to lets us render content in the location in the DOM that we want.

For instance, we can use it by writing:

import React, { useRef, useState } from "react";
import Portal from "react-overlays/Portal";

export default function App() {
  const [show, setShow] = useState(false);
  const containerRef = useRef(null);

  let child = <span>But I actually render here!</span>;

  return (
    <div className="flex flex-col items-center">
      <button type="button" className="btn" onClick={() => setShow(true)}>
        Render Child
      </button>
      <div className="bg-brand-200 p-6 rounded-lg shadow my-4">
        <Portal container={containerRef}>{show && child}</Portal>
      </div>
      <div className="bg-brand-200 p-6 rounded-lg shadow " ref={containerRef} />
    </div>
  );
}

We add the button to toggle the show state to show the content of the portal.

The Portal component has the containerRef to set the container for the portal.

This is let us render the portal content in the location of the containerRef .

useRootClose Hook

The useRootClose hook lets us make a component disappear when we click outside it.

To use it, we write:

import React, { useRef, useState } from "react";
import { useRootClose } from "react-overlays";

export default function App() {
  const ref = useRef();
  const [show, setShow] = useState(false);
  const handleRootClose = () => setShow(false);

  useRootClose(ref, handleRootClose, {
    disabled: !show
  });

  return (
    <div className="flex flex-col items-center">
      <button type="button" className="btn" onClick={() => setShow(true)}>
        Show
      </button>

      {show && (
        <div ref={ref} className="rounded shadow bg-white p-6">
          <span>Click anywhere to dismiss</span>
        </div>
      )}
    </div>
  );
}

We add the button to show the div by setting show to true .

Then we add a div that’s shown when show is true .

We pass in the ref to the div to assign it the ref.

Then we pass in that ref to th useRootClose hook to let us remove the container by setting show to false with the handleRootClose function.

We set the disabled property to make it disabled when show is false .

Conclusion

We can add the Portal component to let us add elements to wherever we want in the DOM.

The useRootClose hook lets us remove components whenever we click outside it.

Categories
React

react-overlays — Tooltips

Tooltips are something that we have to add often into our React app.

To make this task easier, we can use existing component libraries to add them.

In this article, we’ll look at how to add tooltips into our React app with the react-overlays library.

Ovelays

We can add overlays easily with the Overlay component.

To add it, we can write:

import React, { useReducer, useRef, useState } from "react";
import styled from "styled-components";
import Overlay from "react-overlays/Overlay";

const Tooltip = styled("div")`
  position: absolute;
`;

const Arrow = styled("div")`
  position: absolute;
  width: 10px;
  height: 10px;
  z-index: -1;

  &::before {
    content: "";
    position: absolute;
    transform: rotate(45deg);
    background: #000;
    width: 10px;
    height: 10px;
    top: 0;
    left: 0;
  }

  ${(p) =>
    ({
      left: "right: -4px;",
      right: "left: -4px;",
      top: "bottom: -4px;",
      bottom: "top: -4px;"
    }[p.placement])}
`;

const Body = styled("div")`
  padding: 3px 8px;
  color: #fff;
  text-align: center;
  border-radius: 3px;
  background-color: #000;
`;

const PLACEMENTS = ["left", "top", "right", "bottom"];

const initialSstate = {
  show: false,
  placement: null
};

function reducer(state, [type, payload]) {
  switch (type) {
    case "placement":
      return { show: !!payload, placement: payload };
    case "hide":
      return { ...state, show: false, placement: null };
    default:
      return state;
  }
}

export default function App() {
  const [{ show, placement }, dispatch] = useReducer(reducer, initialSstate);
  const triggerRef = useRef(null);
  const containerRef = useRef(null);

  const handleClick = () => {
    const nextPlacement = PLACEMENTS[PLACEMENTS.indexOf(placement) + 1];

dispatch(["placement", nextPlacement]);
  };

  return (
    <div className="flex flex-col items-center" ref={containerRef}>
      <button
        type="button"
        className="btn mb-4"
        id="overlay-toggle"
        ref={triggerRef}
        onClick={handleClick}
        style={{ marginTop: 200, marginLeft: 200 }}
      >
        I am an Overlay target
      </button>
      <Overlay
        show={show}
        rootClose
        offset={[0, 10]}
        onHide={() => dispatch("hide")}
        placement={placement}
        container={containerRef}
        target={triggerRef}
      >
        {({ props, arrowProps, placement }) => (
          <Tooltip {...props} placement={placement}>
            <Arrow
              {...arrowProps}
              placement={placement}
              style={arrowProps.style}
            />
            <Body>
              <strong>{placement}</strong>
            </Body>
          </Tooltip>
        )}
      </Overlay>
    </div>
  );
}

We create the Tooltip component to add the tooltip container.

Then we add the Arrow component to add the arrow for the tooltip.

The Arrow is the child of Tooltip so that it’s attached to the tooltip.

The Body component is a div that has the content of the tooltip.

We can set the placement state to set the placement of the tooltip.

Its value is passed into the placement prop to set the placement.

The possible values are 'left' , 'top' , 'right' , and 'bottom' .

The Overlay component takes a few props to let us adjust it.

The show prop controls when the overlay is shown.

rootClose lets us close the tooltip when we click outside of it.

offset has the horizontal and vertical offsets.

placement has the placement of the tooltip.

containerRef has the container component for the tooltip.

target has the ref for the component to trigger the tooltip.

Now when we click on the button, we see the tooltip show at different locations each time as we change the placement each time.

Conclusion

We can add tooltips into our React app with the Overlays component provided by the React Overlays library.

Categories
React

react-overlays — Dropdowns

Dropdowns are something that we have to add often into our React app.

To make this task easier, we can use existing component libraries to add them.

In this article, we’ll look at how to add dropdowns into our React app with the react-overlays library.

Dropdowns

We can add dropdowns easily with the Dropdown component and the useDropdownMenu and useDropdownToggle hooks.

For example, we can write:

import React, { useState } from "react";
import styled from "styled-components";
import Dropdown from "react-overlays/Dropdown";
import { useDropdownMenu, useDropdownToggle } from "react-overlays";

const MenuContainer = styled("div")`
  display: ${(p) => (p.show ? "flex" : "none")};
  min-width: 150px;
  position: absolute;
  z-index: 1000;
  flex-direction: column;
  border: 1px solid #e5e5e5;
  background-color: white;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
`;

const Menu = ({ role }) => {
  const { show, onClose, props } = useDropdownMenu({
    flip: true,
    offset: [0, 8]
  });
  return (
    <MenuContainer {...props} role={role} show={show}>
      <button type="button" onClick={onClose}>
        Item 1
      </button>
      <button type="button" onClick={onClose}>
        Item 2
      </button>
    </MenuContainer>
  );
};

const Toggle = ({ id, children }) => {
  const [props, { show, toggle }] = useDropdownToggle();
  return (
    <button type="button" className="btn" id={id} {...props} onClick={toggle}>
      {children}
    </button>
  );
};

const DropdownButton = ({ show, onToggle, drop, alignEnd, title, role }) => (
  <Dropdown
    show={show}
    onToggle={onToggle}
    drop={drop}
    alignEnd={alignEnd}
    itemSelector="button:not(:disabled)"
  >
    {({ props }) => (
      <div {...props} className="relative inline-block">
        <Toggle id="example-toggle">{title}</Toggle>
        <Menu role={role} />
      </div>
    )}
  </Dropdown>
);

const ButtonToolbar = styled("div")`
  & > * + * {
    margin-left: 12px;
  }
`;

export default function App() {
  const [show, setShow] = useState(false);

  return (
    <ButtonToolbar className="dropdown-example">
      <DropdownButton
        show={show}
        onToggle={(nextShow) => setShow(nextShow)}
        title={`${show ? "Close" : "Open"} Dropdown`}
      />
      <DropdownButton alignEnd title="Align right" />
      <DropdownButton drop="up" title="Drop up" />
      <DropdownButton role="menu" title="Role 'menu'" />
    </ButtonToolbar>
  );
}

First, we create the MenuContainer component, which has the menu buttons.

It’s the container for dropdown menus.

It checks the show prop to determine whether it’s shown or not.

And we make it absolute positioned to open in the location where we clicked on the menu button.

Next, we create the Menu component to hose the MenuContainer with buttons that calls onClose when we click the buttons.

This will close the dropdowns.

The onClose function is returned by the useDropdownMenu hook.

We pass all the props returned by the hook to the MenuContainer to control whether it’s opened or closed.

The show function lets us show the menu.

Then we add the Toggle component to add the dropdown toggles.

The children prop has the button content.

And the toggle function lets us toggle the menu.

toggle is returned by the useDropdownToggle prop.

We then add the DropdownButton to incorporate the toggle with the menu to let us close the menu when we click on tyhe button or when we click outside.

The ButtonToolbar component is another div to contain the dropdown buttons.

We add the DropdownButtons to open the dropdown when we click on them.

Conclusion

We can add dropdowns into our React app easily with the react-overlays library.