Categories
React

How to Use the Axios HTTP Client in React useEffect Hook?

In many situations, we need to make HTTP requests in our code to get or submit data in our React components.

Axios is a popular HTTP client library that we can use to make HTTP requests easily.

In this article, we’ll look at how to use the Axios HTTP client with the useEffect hook to make HTTP requests.

Use the Axios HTTP Client with the React useEffect Hook

We can make HTTP requests when the component mounts by calling the useEffect hook with an empty array in the 2nd argument.

For instance, we can write:

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

export default function App() {
  const [data, setData] = useState([]);

  const getData = async () => {
    const { data } = await axios.get(`https://yesno.wtf/api`);
    setData(data);
  };

  useEffect(() => {
    getData();
  }, []);

  return <div>{JSON.stringify(data)}</div>;
}

We define the getData function to make a GET request with the axios.get method.

The function is async since axios methods return a promise.

We just pass in a URL to make a GET request.

We’ve to define the getData function outside the useEffect hook since the useEffect callback should be a synchronous function.

The empty array in the 2nd argument means that we make the request only when the component mounts.

We call setData to set the data state and we render the data in the JSX code.

If we want to make HTTP requests when a state or prop changes, then we should pass those into the array in the 2nd argument.

Using the axios-hooks Library

We can also use the axios-hooks library to let us make request with a custom hook that uses the Axios library.

To use it, we install it by writing:

npm install axios axios-hooks

Then we write:

import React from "react";
import useAxios from "axios-hooks";

export default function App() {
  const [{ data, loading, error }, refetch] = useAxios("https://yesno.wtf/api");

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return <div>{JSON.stringify(data)}</div>;
}

We call useAxios to return an array with an object that has the data , loading , and error properties.

data has the response data.

loading has the loading state. It’s true if the request is loading.

error has the error state. It’s true if the request results in an error.

We can call the refetch function to make the request again.

Conclusion

To use Axios with the useEffect hook, we just define an async function and call it inside the useEffect callback.

Alternatively, we can use the useAxios hook provided by the axios-hooks library.

Categories
React

How to Detect Clicks Outside a React Component Using Hooks?

Sometimes, we may want to detect clicks outside a React component for situations like when we want to create a popup menu that closes when we click outside.

In this article, we’ll look at how to detect clicks outside a React component with hooks.

Listening for Clicks on the Page

We can listen for clicks on the page to detect where we clicked on the page.

If we click outside the component, then we remove the React component from the DOM.

Checking for where we clicked on and running code accordingly is called event delegation.

For instance, we can write:

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

const useComponentVisible = (initialIsVisible) => {
  const [isComponentVisible, setIsComponentVisible] = useState(
    initialIsVisible
  );
  const ref = useRef(null);

  const handleClickOutside = (event) => {
    if (ref.current && !ref.current.contains(event.target)) {
      setIsComponentVisible(false);
    }
  };

  useEffect(() => {
    document.addEventListener("click", handleClickOutside, true);
    return () => {
      document.removeEventListener("click", handleClickOutside, true);
    };
  });

return { ref, isComponentVisible, setIsComponentVisible };
};

  const DropDown = () => {
  const { ref, isComponentVisible } = useComponentVisible(true);

  return <div ref={ref}>{isComponentVisible && <p>drop down</p>}</div>;
};

export default function App() {
  return (
    <div>
      <DropDown />
    </div>
  );
}

We create the useComponentVisible hook to watch for clicks on the page.

We first define the isComponentVisible state to track when the component should be visible.

We set that to the initialVisible prop value.

Then we define a ref that’s assigned to the component we want to close when we click outside.

We do the element check in the handleClickOutside function.

This is whee we call contains to check if the element we clicked on is outside the component.

If !ref.current.contains(event.target) is true , then we know we clicked outside the component.

Next, we call document.addEventListener to listen for clicks on the whole page so we can do the check above.

We return a callback that calls removeEventListener to remove the event listener when we unmount the component.

Next, we define the DropDown component to that gets the ref and isComponentVisible state that we returned.

Then we assign the ref to the div so we use the click event listener to do the comparison.

Then finally, we add the DropDown component to App so we can see it.

Now when we click outside the text, the text should go away.

Conclusion

We can detect clicks outside a React component by assign a ref to the component we want to close when we click outside it.

Then we can attach an event handler to document and check whether we clicked outside the element that’s assigned the ref.

Categories
React

How to Update a React Component When Scrolling?

Sometimes, we want to update a React component when we’re scrolling.

In this article, we’ll look at how to update a React component when scrolling.

Add a Scroll Event Listener

We can add a scroll event listener into the useEffect hook to let us watch for scrolling events.

For instance, we can write:

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

export default function App() {
  const [scrollTop, setScrollTop] = useState();
  const [scrolling, setScrolling] = useState();

  useEffect(() => {
    const onScroll = (e) => {
      setScrollTop(e.target.documentElement.scrollTop);
      setScrolling(e.target.documentElement.scrollTop > scrollTop);
    };
    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, [scrollTop]);

  useEffect(() => {
    console.log(scrolling);
  }, [scrolling]);

  return (
    <div>
      {Array(50)
        .fill("foo")
        .map((a, i) => {
          return <p key={i}>{a}</p>;
        })}
    </div>
  );
}

We create the scrollTop and scrolling states with the useState hook.

Next, we call the useEffect hook with a callback that calls addEventListener to listen for scroll events triggered on the browser screen.

The onScroll function sets the scrollTop and scrolling state values.

We know we’re scrolling when e.target.documentElement.scrollTop > scrollTop is true .

We return a callback that calls removeEventListener to remove the scroll event listener when we unmount the component.

And we log the latest value of scrolling in the useEffect callback.

Finally, we return some content we can scroll through in the JSX.

Now when we scroll, we should see the latest value of scrolling logged.

Conclusion

If we want to update a React component when scrolling, we can watch for scroll events that are triggered with an event listener.

Then we can do what we want in the event listener when we’re scrolling in the event handler.

Categories
React

How to Get an Array of DOM Elements with React useRef hook?

Sometimes, we want to assign a ref to an array of rendered DOM elements in our React component.

In this article, we’ll look at how to get an array of DOM elements with the React useRef hook.

Create an Array of Refs and Store it in a Ref

We can create an array of refs and store it in an array of refs.

For instance, we can write:

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

const arr = ["left", "right"];
export default function App() {
  const refs = useRef(arr.map(() => React.createRef()));

  useEffect(() => {
    refs.current[0].current.focus();
  }, []);

  return (
    <div>
      {arr.map((el, i) => (
        <p key={i}>
          <input ref={refs.current[i]} value={el} onChange={() => {}} />
        </p>
      ))}
    </div>
  );
}

We call the useRef hook with the arr.map callback to map arr to an array of refs that we create with React.createRef .

Then we have a useEffect callback that calls refs.current[0].current.focus() to focus on the first element when App mounts.

Finally, we assign the refs in the map callback by assigning the ref property’s value to refs.current[i] .

Now when we mount the component, we should see the first input element having focus.

Alternatively, we can use the useMemo hook to cache the refs by writing:

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

const arr = ["left", "right"];
export default function App() {
  const refs = useMemo(() => arr.map(() => React.createRef()), []);

  useEffect(() => {
    refs[0].current.focus();
  }, []);

  return (
    <div>
      {arr.map((el, i) => (
        <p key={i}>
          <input ref={refs[i]} value={el} onChange={() => {}} />
        </p>
      ))}
    </div>
  );
}

We create the refs the same way, but we wrap the callback with useMemo to cache the created refs.

Then we assign the refs and use them as we did in the previous example.

Conclusion

We can create an array of refs with the createRef function and the useMemo or useRef hook to store them.

Categories
React

How to Share States Between React Components?

Sharing states between components is something we’ve to do a lot with React.

In this article, we’ll look at ways we can use to share states between components in React.

Lift State Up

If 2 child components have the same parent component, then we can life the state we want to share from the child component to the parent.

For instance, we can write:

import React, { useState } from "react";

const DescendantA = ({ count, onCountChange }) => {
  return (
    <button onClick={() => onCountChange((count) => count + 1)}>
      Click me {count}
    </button>
  );
};

const DescendantB = ({ count, onCountChange }) => {
  return (
    <button onClick={() => onCountChange((count) => count + 1)}>
      Click me {count}
    </button>
  );
};

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <DescendantA count={count} onCountChange={setCount} />
      <DescendantB count={count} onCountChange={setCount} />
    </>
  );
}

We have the DescendantA and DescendantB components which both have App as their common ancestor.

So to share a state, we put the state in App .

We have the count state which is defined in App .

And we pass in count to both the DescendantA and DescendantB components.

Also, we pass in onCountChange to DescendantA and DescendantB .

And in DescendantA and DescendantB , we call onCountChange and display the count value.

Context API

Another way to share states between 2 components is to use the Context API.

The Context API lets us share states between any component in the context provider.

For instance, we can write:

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

const CountContext = React.createContext("count");

const DescendantA = () => {
  const { count, setCount } = useContext(CountContext);

  return (
    <button onClick={() => setCount((c) => c + 1)}>Click me {count}</button>
  );
};

const DescendantB = () => {
  const { count, setCount } = useContext(CountContext);

  return (
    <button onClick={() => setCount((c) => c + 1)}>Click me {count}</button>
  );
};

export default function App() {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      <DescendantA />
      <DescendantB />
    </CountContext.Provider>
  );
}

We call React.createContext to create the context.

Then we define DescendantA and DescendantB which calls useContext to get the context.

The value prop of the CountContext.Provider is what’s returned by useContext .

So we can get the count and setCount getter and setter from the returned value.

And in each component, we have the button to set the count .

In App , we wrap CountContext.Provider around DescendantA and DescendantB so we can use the context in both components.

State Management Solution

Another way to share data between multiple components is to use a state management solution.

A popular state management solution for React apps is Redux and React-Redux.

To use it, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import App from "./App";

const rootReducer = (state = 0, action) => {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    default:
      return state;
  }
};

const store = createStore(rootReducer);

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>,
  rootElement
);

App.js

import React from "react";
import { useDispatch, useSelector } from "react-redux";

const DescendantA = () => {
  const dispatch = useDispatch();
  const count = useSelector((state) => state);

  return (
    <button onClick={() => dispatch({ type: "INCREMENT" })}>
      Click me {count}
    </button>
  );
};

const DescendantB = () => {
  const dispatch = useDispatch();
  const count = useSelector((state) => state);

  return (
    <button onClick={() => dispatch({ type: "INCREMENT" })}>
      Click me {count}
    </button>
  );
};

export default function App() {
  return (
    <>
      <DescendantA />
      <DescendantB />
    </>
  );
}

In index.js , we have the rootReducer with that takes the state value and return the latest value of the store based on the action type we dispatch.

We pass in the rootReducer to createStore to create a Redux store.

Then we pass in the store to the React-Redux Provider component to let us use the store in any component inside the Provider .

In App.js , we have DescendantA and DescendantB which calls the useDispatch hook to get the dispatch function.

dispatch lets us dispatch an action to the store.

Also, we call the useSelector hook with a callback to return the value in the callback, which is the latest return value of rootReducer .

When we click the button, we call dispatch with the type set to 'INCREMENT' to commit the action with type 'INCREMENT' .

Now we should see count updated.

Conclusion

There’re several ways we can use to let us share states between components.