Categories
React

How to Fix the ‘React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing’ Error?

The ‘React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing’ error is something that we may see when we’re writing React apps.

In this article, we’ll take a look at how to fix the ‘React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing’ error.

Cause of the Error

This error may occur when we try to pass in an async function as an argument of the first argument of the useEffect hook.

The useEffect hook’s first argument cannot be an async function.

If we want to use an async function, then we should define it outside the function and call it.

Or we can define it inside the function and call it.

For instance, instead of writing:

import { useEffect, useState } from "react";

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

  useEffect(async () => {
    const res = await fetch("https://yesno.wtf/api");
    const data = await res.json();
    setData(data);
  }, []);

  return (
    <div className="App">
      <p>{JSON.stringify(data)}</p>
    </div>
  );
}

We write:

import { useEffect, useState } from "react";

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

  const getData = async () => {
    const res = await fetch("https://yesno.wtf/api");
    const data = await res.json();
    setData(data);
  };

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

  return (
    <div className="App">
      <p>{JSON.stringify(data)}</p>
    </div>
  );
}

Or we can write:

import { useEffect, useState } from "react";

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

  useEffect(() => {
    const getData = async () => {
      const res = await fetch("https://yesno.wtf/api");
      const data = await res.json();
      setData(data);
    };
    getData();
  }, []);

  return (
    <div className="App">
      <p>{JSON.stringify(data)}</p>
    </div>
  );
}

We can’t pass an async function into the useEffect as its first argument because it may lead to race conditions.

Async functions complete in an indeterminate amount of time.

And hooks order matter.

Therefore, we can’t use async functions in the useEffect hooka as its argument.

But this doesn’t prevent us from using them.

We can still call then and catch in the useEffect callback.

So we can write:

import { useEffect, useState } from "react";

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

  useEffect(() => {
    fetch("https://yesno.wtf/api")
      .then((res) => res.json())
      .then((data) => setData(data));
  }, []);

  return (
    <div className="App">
      <p>{JSON.stringify(data)}</p>
    </div>
  );
}

We can have promises in our code, we just can’t write them as async functions if we want to use the function as an argument of useEffect .

Conclusion

To solve the ‘React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing’ error, we shouldn’t pass async function as the first argument of useEffect .

Instead, we can call promises with functions in different ways.

Categories
React

How to compare Old Values and New Values on React useEffect Hook?

Sometimes we want to compare the old and new value of a state change.

In this article, we’ll look at how to get th previous value of a state to the new one.

Create Our Own Hook

We can get the old value of a state with our own hook.

For instance, we can write:

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

const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

export default function App() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  useEffect(() => {
    console.log(prevCount, count);
  }, [prevCount, count]);

  return (
    <div className="App">
      <button onClick={() => setCount((c) => c + 1)}>increment</button>
      <p>{count}</p>
    </div>
  );
}

We create the usePrevious hook with the value parameter which is state we want to get the previous value from,

In the hook, we create a ref with the useRef hook to create a non-reactive property.

Then we add the useEffect hook with a callback that sets the ref.current to value to set the previous value.

And we return ref.current which has the previous value.

Now in App , we create the count state which we want to get the previous value of.

Then we call our usePrevious hook with count to get the previous value of count .

And we watch the value with the useEffect hook to log the prevCount and count .

And we watch the values by passing prevCount and count in the 2nd argument.

Below that, we add the button with the onClick prop set to a function that calls setCount to increment the count.

And we show the count below that.

The useEffect callback should log the prevCount and count values where prevCount should be the previous value of count .

Conclusion

We can watch the previous value of a state by creating our own hook.

Categories
React Projects

Create a Drag and Drop App with React and JavaScript

React is an easy to use JavaScript framework that lets us create front end apps.

In this article, we’ll look at how to create a drag and drop app with React and JavaScript.

Create the Project

We can create the React project with Create React App.

To install it, we run:

npx create-react-app drag-and-drop

with NPM to create our React project.

Create the Drag and Drop App

To create the drag and drop app, we write:

import React, { useState } from "react";

export default function App() {
  const [origin, setOrigin] = useState(["apple", "orange", "grape"]);
  const [target, setTarget] = useState([]);

  const drag = (ev, text) => {
    ev.dataTransfer.setData("text", text);
  };

  const drop = (ev) => {
    const text = ev.dataTransfer.getData("text");
    const index = origin.findIndex((o) => o === text);
    setOrigin((origin) => origin.filter((_, i) => i !== index));
    setTarget((target) => [...target, text]);
  };

  return (
    <div>
      <style>
        {`
          .draggable {
            border: 1px solid black;
            margin-right: 5px;
          }

          #target {
            border: 1px solid black;
            width: 95vw;
            height: 100px;
            padding: 5px;
          }
          `}
      </style>
      <h2>Draggable Elements</h2>
      {origin.map((o) => {
        return (
          <div
            className="draggable"
            draggable
            onDragStart={(e) => drag(e, o)}
            key={o}
            onClick={(e) => e.stopPropagation()}
          >
            {o}
          </div>
        );
      })}

      <h2>Target</h2>
      <div id="target" onDragOver={(e) => e.preventDefault()} onDrop={drop}>
        {target.map((t) => {
          return (
            <div className="draggable" key={t}>
              {t}
            </div>
          );
        })}
      </div>
    </div>
  );
}

We have the origin array state that’s rendered into items that we can drag and drop.

The target array has the dropped items.

Next, we have the drag function that calls ev.dataTransfer.setData to set the data that we want to drag.

The first argument is the key that we can use to get the data.

And the 2nd argument is the data for the corresponding key.

Next, we have the drop method that calls ev.dataTransfer.getData with the key to get the data.

Then we call origin.findIndex to find the index for the data.

Next, we remove the item from the origin with setOrigin called with a callback that returns a copy of the origin array without the index .

And we call setTarget with a callback that returns a copy of the target array with the text value appended to it.

Below that, we have some styles for the drag and drop items and containers.

And below that, we render the origin items.

To make them draggable, we add the draggable prop.

Then we add the onDragStart prop and set a function that calls drag .

onClick is set to a function that calls e.stopPropagation to stop the click event from bubbling to parent and ancestor items.

And below that, we render the target items with the onDragOver and onDrop props.

onDragOver is set to a function that e.preventDefault() so we can do the dropping.

And onDrop is set to drop to let us move data from origin to target .

Conclusion

We can create a drag and drop app easily with React and JavaScript.

Categories
React Projects

Create a JSON to CSV Converter with React and JavaScript

React is an easy to use JavaScript framework that lets us create front end apps.

In this article, we’ll look at how to create a JSON to CSV with React and JavaScript.

Create the Project

We can create the React project with Create React App.

To install it, we run:

npx create-react-app json-to-csv

with NPM to create our React project.

Create the JSON to CSV Converter App

To create the JSON to CSV converter app, we write:

import React, { useState } from "react";

export default function App() {
  const [json, setJson] = useState("");
  const [csv, setCsv] = useState("");

  const convert = async (e) => {
    e.preventDefault();
    const parsedJson = JSON.parse(json);
    if (
      !Array.isArray(parsedJson) ||
      !parsedJson.every((p) => typeof p === "object" && p !== null)
    ) {
      return;
    }
    const heading = Object.keys(parsedJson[0]).join(",");
    const body = parsedJson.map((j) => Object.values(j).join(",")).join("n");
    setCsv(`${heading}${body}`);
  };

  return (
    <div>
      <form onSubmit={convert}>
        <div>
          <label>json</label>
          <textarea
            value={json}
            onChange={(e) => setJson(e.target.value)}
          ></textarea>
        </div>
        <button type="submit">convert</button>
      </form>
      <div>
        <label>csv</label>
        <textarea value={csv}></textarea>
      </div>
    </div>
  );
}

We define the json and csv states to hold the JSON and CSV strings respectively.

Then we define the convert function to convert the json to csv .

In the function, we call e.preventDefault() to do client-side form submission.

Then we parse the json string.

If the JSON isn’t a JSON array or if there are any items that aren’t objects, then we stop running the function.

Otherwise, we proceed to converting the JSON to CSV.

We use the property names as headings.

And we get them with the Object.keys method.

Then we call parsedJson.map to map the values to strings by joining the values with commas and then join them together with a new line character.

Then we call setCsv with the heading and body combined.

Below that, we have a form with the onSubmit prop set to convert .

convert is run when we click the convert button since its type is set to submit .

The text area in the form is bound to the json state with the value and onChange props.

The onChange prop is set to a function that calls setJson with e.target.value .

Below the form, we show a text area that holds the csv .

We display that by setting the value prop to it.

Conclusion

We can create a JSON to CSV converter with React and JavaScript.

Categories
React Projects

Create a Weather App with React and JavaScript

React is an easy to use JavaScript framework that lets us create front end apps.

In this article, we’ll look at how to create a weather app with React and JavaScript.

Create the Project

We can create the React project with Create React App.

To install it, we run:

npx create-react-app weather-app

with NPM to create our React project.

Create the Weather App

To create the weather app, we write:

import React, { useState } from "react";
const APIKEY = "your-key";

export default function App() {
  const [city, setCity] = useState("");
  const [result, setResult] = useState({});

  const getWeather = async (e) => {
    e.preventDefault();
    if (!city) {
      return;
    }
    const res = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${APIKEY}`
    );
    const { main } = await res.json();
    setResult(main);
  };

  return (
    <div>
      <form onSubmit={getWeather}>
        <div>
          <label>city</label>
          <input value={city} onChange={(e) => setCity(e.target.value)} />
        </div>
        <button type="submit">get weather</button>
      </form>
      {result && (
        <div>
          <p>feels like: {result.feels_like}</p>
          <p>humidity: {result.humidity}</p>
          <p>pressure: {result.pressure}</p>
          <p>temperature: {result.temp}</p>
          <p>high: {result.temp_max}</p>
          <p>low: {result.temp_min}</p>
        </div>
      )}
    </div>
  );
}

We define the APIKEY variable set to the API key for the OpenWeather API.

The API key can be obtained for free from https://home.openweathermap.org/api_keys.

Next, we define the city state which we use to bind the inputted value of the city field to it.

The result field has the weather results.

Next, we define the getWeather function to get the weather data by city .

Inside it, we call e.preventDefault() to let us do client-side form submission.

Then we check if city is set.

If it’s set, then we make an API request to the Open Weather API with fetch .

And we call setResult to set the result.

Below that, we create a form with the onSubmit prop set to getWeather so that getWeather is called when we click on the get weather button.

Inside the form, we have an input set to a value and onChange props.

onChange is set to a function to set the city state’s value.

e.target.value has the inputted value.

Below the form, we show the result properties top the user.

Conclusion

We can create a weather app easily with React and JavaScript.