10

My API is getting called twice on the browser page reload as checked in the console. Can you please suggest? I am using Axios to call the API in React.

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

import {Container,Row,Col} from "reactstrap";
import "bootstrap/dist/css/bootstrap.min.css"
import './App.css';

import Axios from "axios";
import MyCard from "./MyCard";

function App() {

  const[details,setDetails]=useState({});

  const fetchDetails=async ()=>{
    const {data}=await Axios.get("https://randomuser.me/api/");
    console.log("RESPONSE:",data);
    const details=data.results[0];
    setDetails(details)
  }

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

  return (
    <Container fluid className="p-4 bg-primary App">
      <Row>
        <Col md={4} className="offset-md-4 mt-4">
          <MyCard details={details}/>
        </Col>
      </Row>
    </Container>
  );
}

export default App;
2

2 Answers 2

32

Fixed 1

This only happens in dev mode. Going live will solve your problem.

Fixed 2

Removing strict mode will solve your problem.

For Example

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

To

root.render(
    <App />
);

Sign up to request clarification or add additional context in comments.

2 Comments

Removing StrictMode is the last thing to do. For a more detailed answer, see stackoverflow.com/questions/72238175/…
Yes, you shouldn't remove it but removing it just to observe what the useEffect is going to do (once or twice) is fine. Which I assume what OP meant,
11

The cause of the issue is in the development of react 18 with strict mode, the useEffect will be mounted -> unmounted -> mounted, which call the API twice.

SOLUTION

You need to clean up requests or events when the component unmounted

For API requests

you need to use AbortController, to abort the request after the component unmounted on the return callback.

useEffect(() => {
  const abortController = new AbortController();
  const result = await axios.get("/api/path", {
    signal: abortController.signal
  });

  return () => {
    abortController.abort()
  }
}, [])

For DOM events

You should have a reference to the handler function and use removeEventListener on the return callback

useEffect(() => {
  const clickHandler = () => {
    // ...code
  }
  
  element.addEventListener('click', clickHandler)

  return () => {
    element.removeEventListener('click', clickHandler)
  }
}, [])

FOR UNCLEANABLE EFFECTS

You can use useRef

const shouldLog = useRef(true);

useEffect(() => {
  if (shouldLog.current) {
    shouldLog.current = false;
    console.log('logged');
  }
}, [])

3 Comments

Mina, your answer for cancel call of axios API is working for me but I'm facing an issue regarding to request header and response header, My issue is I'm passing one's API response header's response to another API's request header, now as per your answer I can cancel API call but but how can I stop to passing same request headers ?
For the eventListener part, it is more controllable in my experience to manipulate onClick property on HTML element than registering one, which might cause trouble with the mounted and unmounted changes.
@hokwanhung, you are right, but adding evenListener is useful in some cases, like for example target a nested element of a library that couldn't be reachable by JSX, or dynamic content, or global event listeners, custom event listeners.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.