How to get useEffect to not re-render infinitely - reactjs

I am trying to render this CardsContainerCopy component after making an AJAX call with Redux-thunk.
If I leave the dependencies array in useEffect empty, the component doesn't render at all.
If I add cartItems to the dependencies array, the components will render but the fetchItems function keeps being called infinitely.
Code:
import React, { useEffect, useState } from "react";
import SingleCard from "./SingleCard";
import { createServer } from "miragejs";
import axios from "axios";
import itemsData from "../../config/ItemsData";
import { useDispatch, useSelector } from "react-redux";
import { selectCartItems } from "./shopSlice";
let server = createServer();
server.get("/api/food", itemsData);
const fetchItems = async (dispatch) => {
const itemsData = await axios.get("/api/food");
dispatch({ type: "shop/fetchedItems", payload: itemsData.data });
};
const CardsContainerCopy = () => {
const [items, setItems] = useState([]);
const dispatch = useDispatch();
const cartItems = useSelector(selectCartItems);
useEffect(() => {
dispatch(fetchItems);
setItems(cartItems);
}, [cartItems]);
return (
<>
{items?.map((item, i) => {
return <SingleCard props={item} key={i} />;
})}
</>
);
};
export default CardsContainerCopy;

Your useEffect function does create an infinite loop, as you're listening to cartItems changes which triggers dispatch again. To avoid infinite re render you can do something like this:
const CardsContainerCopy = () => {
const [items, setItems] = useState([]);
const dispatch = useDispatch();
const cartItems = useSelector(selectCartItems);
useEffect(()=>{
dispatch(fetchItems);
}, [])
useEffect(() => {
setItems(cartItems);
}, [cartItems]);
return (
<>
{items?.map((item, i) => {
return <SingleCard props={item} key={i} />;
})}
</>
);
};

Related

how use get method in react js

this is my react code here I fetch the data from the backend using mongo. my data is appearing in the console but not appearing on the web page it's showing `users.map is not a function. but if I try the jsonplaeholder API then its work properly.
import React, { useEffect, useState } from "react";
const Get = () => {
const [users,setUsers] = useState([]);
const getAllUser = async () => {
const response = await fetch("/get");
setUsers(await response.json());
console.log(users);
};
useEffect(() => {
getAllUser();
},[]);
return (
<>
{ users.map((ce) =>
<div key={ce.id}>
<h2>{ce.name}</h2>
<p>{ce.email}</p>
</div>)}
</>
)
}
export default Get;
this is the db data
{"status":"success","results":2,"data":{"users":[{"_id":"6134fcc6eddae0ec522fecd7","name":"ram ","email":"ram#gmail.com","number":9455294552,"__v":0},{"_id":"61364d918a8ab07512094443","name":"rawal","email":"rawal#gmail.com","number":9309304400,"__v":0}]}}
You need to properly set your state with res.data.users as follows.
import React, { useEffect, useState } from "react";
const Get = () => {
const [users, setUsers] = useState([]);
const getAllUser = async () => {
const response = await fetch("/get");
response.json().then((res) => setUsers(res.data.users));
console.log(users);
};
useEffect(() => {
getAllUser();
}, []);
return (
<>
{users.map((ce) => (
<div key={ce.id}>
<h2>{ce.name}</h2>
<p>{ce.email}</p>
</div>
))}
</>
);
};
export default Get;

How do I properly set up an API call using useEffect?

Here is my entire component. In the console the correct data is showing up at "data" but when I try to run map on it it says "map is not a function." The 16 items in the console are the correct beaches.
import React, {useState, useEffect} from 'react';
import axios from 'axios';
export default function Beaches() {
const [data, setData] = useState({beaches: []})
// const [hasError, setErrors] = useState(false)
useEffect(() => {
const fetchBeaches = async () => {
const result = await axios('http://localhost:3000/beaches');
setData(result.data);}
fetchBeaches();
}, [])
console.log(data)
return (
<ul>
{data.beaches.map(beach => (
<button>{beach.name}</button>
))}
</ul>
)
}
Because you're not setting the beaches data in state correctly.
Replace useEffect code with this:
useEffect(() => {
const fetchBeaches = async () => {
const result = await axios('http://localhost:3000/beaches');
setData({beaches: result.data});
}
fetchBeaches();
}, [])
furthermore, you can improve the state structure of beaches data:
import React, { useState, useEffect } from "react";
import axios from "axios";
export default function Beaches() {
const [beaches, setBeaches] = useState([]);
// const [hasError, setErrors] = useState(false)
useEffect(() => {
const fetchBeaches = async () => {
const result = await axios("http://localhost:3000/beaches");
setBeaches(result.data);
};
fetchBeaches();
}, []);
return (
<ul>
{beaches.map((beach) => (
<button>{beach.name}</button>
))}
</ul>
);
}

Why does setItems function (useState hook) work when used inside a function inside useEffect but not when it is used inside useEffect directly?

In the example why does setItems work here:
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [items, setItems] = useState([]);
useEffect(() => {
const fetchItems = async () => {
const result = await axios.get(
`https://www.breakingbadapi.com/api/characters`
);
setItems(result.data);
};
fetchItems();
}, []);
return (
<div>
{items.map(item => (
<div key={item.char_id}>{item.name}</div>
))}
</div>
);
}
https://codesandbox.io/s/boring-butterfly-2upbp
but not here (instead it returns a TypeError items is undefined):
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [items, setItems] = useState([]);
useEffect(() => {
const fetchItems = async () => {
const result = await axios.get(
`https://www.breakingbadapi.com/api/characters`
);
return result;
};
const result = fetchItems();
setItems(result.data);
}, []);
return (
<div>
{items.map(item => (
<div key={item.char_id}>{item.name}</div>
))}
</div>
);
}
https://codesandbox.io/s/compassionate-lake-7iji5
You have to use then function to get the result.
fetchItems().then(result => setItems(result.data))
You declared an async function in your useEffect hook thus fetchItems will return a promise. But since useEffect function argument does not accept async functions, it's better to resolve the promise first before setting the state
const fetchItems = async () => {
const result = await axios.get(
`https://www.breakingbadapi.com/api/characters`
);
return result;
};
const result = fetchItems(); // result is a promise.
setItems(result.data);
In the above snippet, fetchItems is an async function so it returns a promise. So when you use result.data, you're trying to access data on the Promise but not on the resolved value.
The main reason is because fetchItems returns a promise (async/await).
But there is another problem because you would need to wait that the promise was resolved so you need to add await to fetchItems but this cannot be done because useEffect must return a clean-up function.
CORRECT:
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [items, setItems] = useState([]);
// Also it's valid to create it directly inside the useEffect
const fetchItems = useCallback(async () => {
const result = await axios.get(
`https://www.breakingbadapi.com/api/characters`
);
setItems(result.data);
}, [setItems])
useEffect(() => {
fetchItems();
}, [fetchItems]);
return (
<div>
{items.map(item => (
<div key={item.char_id}>{item.name}</div>
))}
</div>
);
}
WRONG:
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [items, setItems] = useState([]);
// WRONG: useEffect is not a clean-up function.
useEffect(async () => {
const fetchItems = async () => {
const result = await axios.get(
`https://www.breakingbadapi.com/api/characters`
);
return result;
};
const result = await fetchItems();
setItems(result.data);
}, []);
return (
<div>
{items.map(item => (
<div key={item.char_id}>{item.name}</div>
))}
</div>
);
}

Correct way to use useEffect() to update when data changes

The useEffect below renders, fetches data, and displays it once (using an empty array for 2nd parameter in useEffect).
I need it to rerun useEffect everytime the user changes data to the database (when user uses axios.post).
What i've tried
using [tickets], but that just causes the useEffect to run infinitly
also using [tickets.length] and [tickets, setTickets]
trying to use props as parameter but didnt find anything useful
import React, { useState, createContext, useEffect } from "react";
import axios from "axios";
export const TicketContext = createContext();
export const TicketProvider = (props) => {
console.log(props);
const [tickets, setTickets] = useState([]);
useEffect(() => {
getTickets();
console.log("1", { tickets });
}, []);
const getTickets = async () => {
const response = await axios.get("http://localhost:4000/tickets/");
setTickets(response.data);
};
return <TicketContext.Provider value={[tickets, setTickets]}>{props.children}
</TicketContext.Provider>;
};
import React from "react";
import { useState, useEffect, useContext } from "react";
import Ticket from "../Ticket";
import { TicketContext } from "../contexts/TicketContext";
import AddBacklog from "../addData/AddBacklog";
const TicketDisplay = (props) => {
const [tickets, setTickets] = useContext(TicketContext);
return (
<div className="display">
<p>Antony Blyakher</p>
<p>Number of Tickets: {tickets.length}</p>
<div className="backlog">
<h1>Backlog</h1>
{tickets.map((currentTicket, i) => (
<div className="ticketBlock">
<Ticket ticket={currentTicket} key={i} />
</div>
))}
</div>
</div>
);
const AddBacklog = (props) => {
const [tickets, setTickets] = useState("");
...
axios.post("http://localhost:4000/tickets/add", newTicket).then((res) => console.log(res.data));
setTickets((currentTickets) => [...currentTickets, { name: name, status: "backlog", id: uuid() }]);
};
You'll need to watch for tickets and return if it has data to not cause infinite loop:
useEffect(() => {
if (tickets.length) return // so, we call just once
getTickets();
console.log("1", { tickets });
}, [tickets]);
const fetchData = () => {
axios.get("http://localhost:7000/api/getData/").then((response) => {
console.log(response.data);
if (response.data.success) {
SetIsLoading(false);
}
setDataSource(response.data.data);
});
};
useEffect(() => {
fetchData();
if (fetchData.length) fetchData();
}, [fetchData]);
by this you can fetch the data in real-time as any change in data occurs.

change in hook state not updating value in template literals

I am new to hooks and is coming after learning react with classes, so a bit lost. in the below code I am changing setDog to Husky which should then tell the API call to search and fetch me pic of a husky. But its not happening despite the change in dog. Can anyone see where I am going wrong?
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function ApiCalls() {
const [ data, setData ] = useState();
const [ dog, setDog ] = useState('labrador');
useEffect(() => {
const fetchData = async () => {
const result = await axios(`https://dog.ceo/api/breed/${dog}/images`);
setData(result.data.message[0]);
};
fetchData();
}, []);
const Husky = () => {
setDog('husky');
};
return (
<div>
<img alt={''} src={data} />
<button onClick={Husky}>Retrieve Husky</button>
</div>
);
}
Your useEffect sensivitylist is [], so this useEffect just run on component mount that the dog variable is labrador. So after you change dog on button click nothings new will be fetched from server. Change your code as follow:
useEffect(() => {
const fetchData = async () => {
const result = await axios(`https://dog.ceo/api/breed/${dog}/images`);
setData(result.data.message[0]);
};
fetchData();
}, [dog]);
useEffect only run once because the dependency array is [] empty. So when you change dog it wont trigger. To fix this add dog to useEffect dependency
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function ApiCalls() {
const [ data, setData ] = useState();
const [ dog, setDog ] = useState('labrador');
useEffect(() => {
const fetchData = async () => {
const result = await axios(`https://dog.ceo/api/breed/${dog}/images`);
setData(result.data.message[0]);
};
fetchData();
}, [dog]);
const Husky = () => {
setDog('husky');
};
return (
<div>
<img alt={''} src={data} />
<button onClick={Husky}>Retrieve Husky</button>
</div>
);
}
Do this
const fetchData = async (input) => {
const result = await axios(`https://dog.ceo/api/breed/${input}/images`);
setData(result.data.message[0]);
};
useEffect(() => fetchData(dog), []);

Resources