My App's "React Hook useEffect has a missing dependency" warning - reactjs

I have a simple app for searching a db of Albums by artist.
Three components: App, AlbumList, AlbumSearch.
The App component defines getAlbumsByArtist to fetch and update albums,
and passes albums as prop to AlbumList.
App defines getAlbumsByArtist to pass as prop to AlbumSearch.
In AlbumSearch, when the artist is changed, I useEffect to call getAlbumsByArtist.
The app works, but in AlbumSearch I get a warning of
React Hook useEffect has a missing dependency: 'getAlbumsByArtist'.
How can I refactor to fix this warning?
import React, { useState, useEffect } from "react";
function App() {
const [albums, setAlbums] = useState(false);
useEffect(() => {
getAlbums();
}, []);
function getAlbums() {
fetch("http://localhost:3001")
.then((response) => response.json())
.then((data) => setAlbums(data));
}
function getAlbumsByArtist(artist) {
fetch(`"http://localhost:3001"/albums?artist=${artist}`)
.then((response) => response.json())
.then((data) => setAlbums(data));
}
return (
<div>
<h2>Albums</h2>
<AlbumSearch getAlbumsByArtist={getAlbumsByArtist} />
<AlbumList albums={albums} />
</div>
);
}
function AlbumSearch({ getAlbumsByArtist }) {
const [artist, setArtist] = useState("");
useEffect(() => {
getAlbumsByArtist(artist);
}, [artist]);
const handleArtist = (e) => {
e.preventDefault();
setArtist(e.target.value);
};
return (
<div className={`album-search ${showSearchClass}`}>
<div>
<label>Artist contains: </label>
<input
id="searchArtist"
type="text"
value={artist}
onChange={handleArtist}
/>
</div>
</div>
);
}
function AlbumList({ albums }) {
return (
<div>
<header>
<div>Id</div> <div>Artist</div> <div>Title</div>{" "}
</header>
<div>
{albums &&
albums.map((album) => {
return (
<div key={album.id}>
<div>{album.id}</div>
<div>{album.artist}</div>
<div>{album.title}</div>
</div>
);
})}
</div>
</div>
);
}

As react will guarantee that a setState() function will never change, you can simply add the getAlbumsByArtist function to your useEffect() dependencies to get the same result.
useEffect(() => {
getAlbumsByArtist(artist);
}, [getAlbumsByArtist, artist]);
Another way would be to simply irgnore the warning by:
useEffect(() => {
getAlbumsByArtist(artist);
}, [getAlbumsByArtist, artist]); // eslint-disable-line react-hooks/exhaustive-deps

Related

results.map is not a function (Consider adding an error boundary to your tree to customize error handling behavior.)

I have this what may seem like a simple problem for more experienced developers but it has been irritating me for quite a while.
I keep having .map is not a function, although it clearly is. see the code below
I am iterating over the results state, but it doesn't seem to work
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<App.js>>>>>>>>>>>>>>>>>>>>>>>>>>>>
import "./App.css";
import React, { useEffect, useState } from "react";
import ContactCard from "./ContactCard";
const App = () => {
const [results, setResults] = useState([]);
useEffect(() => {
fetch("https://randomuser.me/api/?results=5")
.then((response) => response.json())
.then((data) => {
console.log(data);
setResults(data);
});
}, []);
return (
<div>
{results.map((result, i) => {
return (
<ContactCard
key={i}
avatarUrl={result.picture.large}
name={result.name}
email={result.email}
age={result.dob.age}
/>
);
})}
</div>
);
};
export default App;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>
import React, { useState } from "react";
const ContactCard = (props) => {
const [showAge, setShowAge] = useState(false);
const showAgefn=()=>{
setShowAge(!showAge)
}
return (
<div className="contact-card">
<img src={props.avatarUrl} alt="profile" />
<div className="user-details">
<p>Name: {props.name}</p>
<p>Email: {props.email}</p>
<button onClick={showAgefn}>Show age</button>
{showAge && <p>Age: {props.age}</p>}
</div>
</div>
);
};
export default ContactCard;
Try this
{results.results.map((result, i) => {
return (
<ContactCard
key={i}
avatarUrl={result.picture.large}
name={result.name}
email={result.email}
age={result.dob.age}
/>
);
}
i prefer to rename my state to [data, setData] then I can use data.results instead of results.results
This issue is that the response you are getting from https://randomuser.me/api/?results=5 is like as follows
{
"results": [...],
"info": {...}
}
So in your useEffect just modify the following
useEffect(() => {
fetch("https://randomuser.me/api/?results=5")
.then((response) => response.json())
.then((data) => {
console.log(data);
setResults(data.results); // Just modify this line
});
}, []);
All Other things are perfectly fine
Hope it Helps
First results keyword is the state and Second results keyword is for the array.
Don't Forget to use ?.map as if the map is null it won't return any error. It's a check if there is any data in map or not.
{results.results?.map((result, i) => {
return (
<ContactCard
key={i}
avatarUrl={result.picture.large}
name={result.name}
email={result.email}
age={result.dob.age}
/>
);
}
Saving the state like so [users, setUsers]
Then adding Array.from inside the curly brackets seem to have solved the issue
return (
<div>
{Array.from(users.map((user, i) => {
return (
<ContactCard
key={i}
avatarUrl={user.picture.large}
name={user.first}
email={user.email}
age={user.dob.age}
/>
);
}))}
</div>
);

how to show a new todo-item without refreshing the page?

I tried a lots of things , and this problem does not seem to go away , can someone help me with this ??
this is my app component :
function App() {
const [todo, setTodo] = useState([]);
async function getTodo() {
try {
const todo = await axios.get("http://localhost:5000/api/todos");
// console.log(todo.data)
setTodo(todo.data);
} catch (error) {
console.log("something is wrong");
}
}
useEffect(() => {
// Update the document title using the browser API
getTodo();
}, []);
return (
<div className="App">
<h1>My Todo List</h1>
<h2>My Todo List</h2>
<Task Todor={todo} />
<Write />
</div>
);
}
export default App;
and this is my todos component :
function Todos({ Todor }) {
return (
<div className="Todos">
{Todor.map(T => <Todo post={T} />)}
</div>
);
}
export default Todos;
and this is my todo component :
function Todo({ post }) {
return (
<div className="Todo">
<h2>{post.title}</h2>
</div>
);
}
export default Todo ;
and this my add component :
export default function Write() {
const [inputText, setInputText] = useState({
title: ""
});
function handleChange(e) {
setInputText({
...inputText,
[e.target.name]: e.target.value,
});
}
const [status, setStatus] = useState(false);
async function addItem(e) {
e.preventDefault();
const res = await axios.post("http://localhost:5000/api/todos", inputText);
setInputText(inputText)
console.log("response:", res)
setStatus(true);
setInputText("");
}
return (
<div className="container">
<div className="form">
<input onChange={handleChange} type="text" name="title" />
<button onClick={addItem}>
<span>Add</span>
</button>
</div>
</div>
);
}
the new items dont show until I refresh the page , how to do that without refreshing ?
because obviously that defeats the purpose of React !!
useEffect(() => {
// Update the document title using the browser API
getTodo();
}, []);
The code inside useEffect with empty dependencies array [] only runs on the first render, to run it on every render you should remove the empty array dependencies.
useEffect(() => {
// Update the document title using the browser API
getTodo();
});
Note: It is not a best practice because your component will invoke getTodo() every time rerendered. In your case, you can use a state variable to control where to re-run the getTodo funtion e.g:
const [isAddedSuccess, setIsAddedSuccess] = useState(false)
Everytime you add new item successfully, just setIsAddedSuccess(true) and your useEffect should look like below:
useEffect(() => {
// Update the document title using the browser API
if (isAddedSuccess) getTodo();
}, [isAddedSuccess]);

Issue with API GET response format

I am making a get request to an api and then trying to map over the request. My issue is that the response is changing from data in this usual [{...},{...}] to '[[Object Object][Object Object]]' so that when I try to map over the array, it is throwing an error. I have tried using JSON.stringify() but it does not work. Here is my code:
---- Home.js
import React, { useState, useEffect } from "react";
const Home = () => {
const [tableData, setTableData] = useState([]);
useEffect(() => {
const url =
"https://api.football-data.org/v2/competitions/2021/standings?standingType=HOME";
const fetchItems = async () => {
const apiResult = await fetch(url, {
headers: {
"X-Auth-Token": process.env.REACT_APP_API_KEY,
},
})
.then((res) => res.json())
.catch((err) => console.error(err));
setTableData(apiResult.standings[0].table);
};
fetchItems();
}, []);
return (
<div className="home-container">
<Hero />
<Table data={tableData} />
<Predictions />
<Footer />
</div>
);
};
------ Table.js
import TableList from "./TableList";
const Table = ({ data }) => {
return (
<section className="table" id="table">
<h1 className="table-header">Table</h1>
<div className="table-container">
<TableList data={data} />
</div>
</section>
);
};
--------- TableList.js
import TableCard from "./TableCard";
const TableList = ({ data }) => {
return (
<div className="table-list">
{data.map((index, teamData) => {
return <TableCard key={index} data={teamData} />;
})}
</div>
);
};
If I console log the mapped data, I get '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20',
instead of the data that I initially receive in the fetch request that is [{position:1, team: {...}, gamesPlayed:9}, {...}].
I have never had this issue before when using a GET request to an API so any and all help is appreciated. Sorry in advance for the poor formatting.

React hook is not calling API after first load sometimes not at all

I have a very simple website that I'm using based off React, hosted on Firebase. On localhost and on my hosted site, I have ONE page that loads an API. Whenever I load that API, that page stops working usually after the first attempt, sometimes it doesn't work at all until I change the return statement to empty and back again. I was using class components and the error never occurred BUT I was unable to access a lot of the information in data.
The problem: My hook is not setting the state, data.
the error: TypeError: Cannot read property 'volumeInfo' of null
the question: How do I get my hook to call every time on page load?
cross reference error: No response from Fetch API in React useEffect hook I looked at this post, hopefully for guidance, but it's still not loading.
the code:
import '../App.css';
import Navigation from './Navigation';
import React, { useState, Component, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const Milestone1 = () => {
const [data, setData] = useState(null);
useEffect(() => {
const callAPI = async () => {
try {
fetch("https://www.googleapis.com/books/v1/volumes/Wfan6L9RGgYC")
.then((res) => res.json())
.then((data) => setData(data));
} catch (e) {
console.log(e);
}
};
callAPI();
}, []);
console.log(data);
return <>{(
<div className="school">
<header className="App-header">
</header>
<center>
<div className="navigation">
<Navigation />
</div>
<body className="App-body">
<body className="App-body">
<h2>Milestone 1</h2>
<h5>Title: {data && data.volumeInfo.title}</h5>
<img src={data.volumeInfo.imageLinks.smallThumbnail} alt="image" />
<h5>Authors: {data && data.volumeInfo.authors} Publish info: {data && data.volumeInfo.publisher} {data && data.volumeInfo.publishedDate} Country: {data && data.saleInfo.country}</h5>
<h5>Rating: {data && data.volumeInfo.averageRating}</h5>
</body>
<NavLink to="/Milestone1part2">Milestone 1 part two</NavLink><br></br>
</body>
</center>
</div>
)}</>;
}
export default Milestone1;
my other hook that's dumbed down:
const [data, setData] = useState(null);
useEffect(() => {
fetch("https://www.googleapis.com/books/v1/volumes/Wfan6L9RGgYC")
.then((res) => res.json())
.then((data) => setData(data));
}, []);
I've read similar posts, and usually they have a button in the page or something that can recall the API, but 1) it's not an interactive page 2) the whole page is not loading.
Thanks in advance.
You are getting this error because the data starts as null value, and then you try to render something like null.volumeInfo.imageLinks.smallThumbnail in here <img src={data.volumeInfo.imageLinks.smallThumbnail} alt="image" />
Try to add a loading component to handle the null data and to avoid this code {data && data.something}.
if (!data) {
return <div>Loadding...</div>;
}
Example:
https://codesandbox.io/s/reverent-firefly-ej0yk?file=/src/App.js:0-1276
import React, { useState, useEffect } from "react";
const Milestone1 = () => {
const [data, setData] = useState(null);
useEffect(() => {
const callAPI = async () => {
try {
fetch("https://www.googleapis.com/books/v1/volumes/Wfan6L9RGgYC")
.then((res) => res.json())
.then((data) => setData(data));
} catch (e) {
console.log(e);
}
};
callAPI();
}, []);
console.log(data);
if (!data) {
return <div>Loadding...</div>;
}
return (
<div className="school">
<header className="App-header"></header>
<center>
<div className="navigation"></div>
<body className="App-body">
<body className="App-body">
<h2>Milestone 1</h2>
<h5>Title: {data.volumeInfo.title}</h5>
<img src={data.volumeInfo.imageLinks.smallThumbnail} alt="image" />
<h5>
Authors: {data.volumeInfo.authors} Publish info:{" "}
{data.volumeInfo.publisher} {data.volumeInfo.publishedDate}{" "}
Country: {data.saleInfo.country}
</h5>
<h5>Rating: {data.volumeInfo.averageRating}</h5>
</body>
</body>
</center>
</div>
);
};
export default Milestone1;

React: "TypeError: undefined is not a function"

I am getting this problem TypeError: undefined is not a function and I did not recognize the error,
this is my code.
I have included the full code of this component in order to be clear
import React, {Component, useState,useEffect} from "react";
function Counter() {
const [searchTerm, setSearchTerm] = useState("");
const[materiel,setMateriels]=useState([]);
useEffect(() => {
fetch("http://localhost:5000/materiels")
.then((res) => res.json())
.then((data) => {
setMateriels(data);
console.log(materiels);
})
.catch(console.log);
}, []);
}
class searchMateriel extends Component {
render() {
return (
<div className="container">
<div className="col-xs-12">
<input type="text" placeholder="Search..." onChange={event => {
this.setState({searchTerm: event.target.value});
}}/>
{this.state.materiels
.filter((val) => val.nom.startsWith(this.statesearchTerm))
.map((val, key) => {
return (
<div className="user" key={{key}}>
<p>{val.nom}</p>
</div>
);
})}
</div>
</div>
);
}
state = {
materiels: [],
searchTerm: "",
}
componentDidMount() {
fetch('http://localhost:5000/materiels')
.then(res => res.json())
.then((data) => {
this.setState({materiels: data})
console.log(this.state.materiels)
})
.catch(console.log)
}
}
export default searchMateriel;
I have updated the code but still not working.
It is showing this error
Line 11:29: 'materiels' is not defined
The error is in the last line of my code, does anyone please have an idea?
Thanks in advance
Your code/use-case is wrong, in this code Counter is a functional component, so React except that to return some JSX's...
you get undefined because Counter returns nothing then you want to access the setSearchTerm method... basically, you will get the undefined is not a function react js error.
to store the input value in the state, you can pass the parent setState to the children or define a state in it.
NOTE: if you merely want to store your state in another place and process it, you can use hooks.
Counter in the code above is just a function, which actually doesn't return anything. Also Counter doesn't have setSearchTerm method which you are trying to access. You cannot use a hook outside of the top level of a functional component or hook see docs
As you are using class-based components, you should use setState method to update your state or switch to functional component and use useState hook
I don't see any usage of searchTerm in your code, but I am assuming, that you will add a function to your filter function.
class searchMateriel extends Component {
render() {
return (
<div className="container">
<div className="col-xs-12">
<input
type="text"
placeholder="Search..."
onChange={(event) => {
this.setState({ searchTerm: event.target.value });
}}
/>
{this.state.materiels
.filter((val) => val.nom.startsWith(this.statesearchTerm))
.map((val, key) => {
return (
<div className="user" key={{ key }}>
<p>{val.nom}</p>
</div>
);
})}
</div>
</div>
);
}
state = {
materiels: [],
searchTerm: "",
};
componentDidMount() {
fetch("http://localhost:5000/materiels")
.then((res) => res.json())
.then((data) => {
this.setState({ materiels: data });
console.log(this.state.materiels);
})
.catch(console.log);
}
}
export default searchMateriel;
Also, I wanted to mention that in class-based components state is merged when you are using setState but when you are using useState hook state is completely replaced
If you would like to replace your class-based component with functional one, it could look like this:
const searchMateriel = () => {
const [searchTerm, setSearchTerm] = useState("");
const [materiels, setMateriels] = useState([]);
useEffect(() => {
fetch("http://localhost:5000/materiels")
.then((res) => res.json())
.then((data) => {
setMateriels(data);
console.log(materiels);
})
.catch(console.log);
}, []);
return (
<div className="container">
<div className="col-xs-12">
<input
type="text"
placeholder="Search..."
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
{materiels
.filter((val) => val.nom.startsWith(searchTerm))
.map((val, key) => {
return (
<div className="user" key={{ key }}>
<p>{val.nom}</p>
</div>
);
})}
</div>
</div>
);
};
export default searchMateriel;
More about useEffect you can read here
seeing you are not defining state in constructor. this may be the reason. Constructor loads before component is mounted. https://reactjs.org/docs/react-component.html#constructor

Resources