updating data in useState constant | React - reactjs

I'm trying to make "load more" pagination in react app fetching data from google books api via axios.
Firstly I'm grabbing data by submitting the form and setting it into the bookResponse const and then putting the value of bookResponse const into the booksArray const by setBooksArray(bookReponse).
Secondly I need to show more book's by clicking the 'Load More' button. By click I make a new request to the api and receive the response. I'm updating the bookResponse const with new data and trying to update the booksArray const by setBooksArray(booksArray + bookResponce) but it returns errror "books.map is not a function". How can i solve the problem?
{books && books.map(book => (
<div key={book.id}>
<img alt="book's thumbnail" src={book.volumeInfo.imageLinks.thumbnail} />
<div>
<h5>{book.volumeInfo.title}</h5>
<p>{book.volumeInfo.authors}</p>
<p>{book.volumeInfo.categories}</p>
</div>
</div>
))
}
function App() {
const [bookName, setBookName] = useState('')
const [bookCategory, setBookCategory] = useState('all')
const [bookFilter, setBookFilter] = useState('relevance')
const [bookResponse, setBookResponse] = useState([])
const [booksArray, setBooksArray] = useState([])
const apiKey = ('MY_KEY')
const [showLoadMoreButton, setShowLoadMoreButton] = useState(false)
const handleSubmit = (e) => {
e.preventDefault()
console.log(bookName, bookCategory, bookFilter)
axios.get(endpoint)
.then(res => {
console.log(res.data.items)
setBookResponse(res.data.items)
setBooksArray(bookResponse)
console.log(bookResponse ,booksArray)
})
setShowLoadMoreButton(true)
}
const handleLoadMore = (e) => {
e.preventDefault()
axios.get(endpoint)
.then(res => {
console.log(res.data.items)
console.log(bookResponse)
setBookResponse(bookResponse)
setBooksArray(booksArray + bookResponse)
})
}

You don't + to add 2 arrays, you can do something like this.
setBooksArray( prev => [ ...prev, ...bookResponse ] )
....
return (
....
{ booksArray.map(book => ( .....) }
.....
)

if booksArray and bookResponse types are array you can use this:
setBooksArray([...booksArray,...bookResponse])

When you do booksArray + bookResponse you are creating a string,
example would be
const arr1 = [1,2,3]
const arr2 = [4,5,6]
console.log(arr1 + arr2)
"1,2,34,5,6"
The solution would be to spread both of the arrays like this:
setArray([...arr1, ...arr2])

Related

[solution]Why "Cards" still doesn't receive the passed value?

Why "Cards" still doesn't receive the passed value from selectedCountryInfo
I just tried passing await to the variable, still doesn't work. "Cards" still don't receive value.
<----solution: when there are have 2 setStates, should use 2 variables, not use 1 variable.(I guess if there are 3 setStates use 3 variables and so on)
I've been thinking about it for over 12 hours and can't think of a solution.
Because the default value of useState cannot put async/await.
(fetchedCountries is array,selectedCountryInfo is object)
const App = () => {
const [fetchedCountries, setFetchedCountries] = useState([]);
const [selectedCountryInfo, SetSelectedCountryInfo] = useState();
useEffect(() => {
const myCountries = async () => {
const countries = await worldWideCountries();
setFetchedCountries(countries);
SetSelectedCountryInfo(fetchedCountries[0]);
};
myCountries();
}, []);
return (
<div>
<Cards selectedCountryInfo={selectedCountryInfo} />
</div>
);
Solution:(from the 3 lines)
const countries = await worldWideCountries();
setFetchedCountries(countries);
const ww = countries[0];
SetSelectedCountryInfo(ww);
You probably want to use conditional rendering
const App = () => {
const [fetchedCountries, setFetchedCountries] = useState([]);
const [selectedCountryInfo, SetSelectedCountryInfo] = useState();
useEffect(() => {
const myCountries = async () => {
setFetchedCountries(await worldWideCountries());
SetSelectedCountryInfo(fetchedCountries[0]);
};
myCountries();
}, []);
return (
<div>
{ selectedCountryInfo && <Cards selectedCountryInfo={selectedCountryInfo} /> }
</div>
);
}

How to display an array in JSX from a function in React?

I implemented a function where I fetch all Docs from a Firebase collection on a click.
Now I want to display each doc I fetched in a <div> container in JSX. When I try to take the array and display it, I´m getting the error that the array is not found.
This is my code:
async function getAllDivs(){
const querySnapshot = await getDocs(collection(db, "Div"))
const allDivs = [];
querySnapshot.forEach(doc => {
allDivs.push(doc.data().DivContent);
});
}
You would have to return the array from the function, because of the "scope".
Example:
//your current function
async function getAllDivs(){
const querySnapshot = await getDocs(collection(db, "Div"));
return querySnapshot.map((doc) => doc.data().DivContent);
}
//your component
let divs = getAllDivs(); //you can now use "divs" in this scope
return (
<>
divs.map((current_div) => { <div>{current_div}</div> })
</>
)
Also, I suggest against pushing data to an array labeled as const, as it could be confusing for someone else reading your code.
I think you could use something like this:
const MyComponent = () => {
const [docs, setDocs] = useState();
const onClickHandler = async () => {
const docs = await getDocs(collection(db, "Div"));
setDocs(docs);
}
return (
<>
<button onClick={onClickHandler}>Get docs</button>
{docs && docs.map(doc => (
<div>{doc.data().DivContent}</div>
))}
</>
)
}
If DivContent contains HTML you can use dangerouslySetInnerHTML.

React, unexpected multiple result when using map and fetch

I am building Weather App, my idea is to save city name in localStorage, pass a prop to child component, then iterate using map and display each in seperate child of the first child
The problem is that displayed data doubles/triples on render(depending on component when render occurs) so when I have for example city London and add city Berlin it will render:
London,London,Berlin
The problem is not in AddCity component, it's working correctly but in this mix of asynchronous setState/fetching and maping
Please see the code below
App(parent component)
const App = () => {
const [cities, setCities] = useState([]);
const addCity = (newCity)=>{
console.log('adding')
setCities([...cities, newCity]);
let cityId = localStorage.length;
localStorage.setItem(`city${cityId}`, newCity);
}
useEffect(() => {
loadCityFromLocalStore()
}, [])
const loadCityFromLocalStore =()=>{
setCities([...cities, ...Object.values(localStorage)])
}
return (
<div>
<Header />
<AddCity addCity={addCity}/>
<DisplayWeather displayWeather={cities}/>
</div>
)
}
DisplayWeather (first child)
const DisplayWeather = ({displayWeather}) => {
const apiKey = '4c97ef52cb86a6fa1cff027ac4a37671';
const [fetchData, setFetchData] = useState([]);
useEffect(() => {
displayWeather.map(async city=>{
const res =await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=>[...fetchData , data]));
})
}, [displayWeather])
return (
<>
{fetchData.map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
}
Weather component
const Weather = ({data}) => {
return (
<li>
{data.name}
</li>
)
}
It looks like the problem comes from calling setFetchData for cities that you already added previously.
One easy way to fix it would be to store fetch data as an object instead of a dictionary so that you just override the data for the city in case it already exists (or maybe even skip the fetch as you already have the data).
For example:
const [fetchData, setFetchData] = useState({});
useEffect(() => {
displayWeather.map(async city=>{
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
})
}, [displayWeather])
Then, to map over fetch data you can just use Object.values:
return (
<>
{Object.values(fetchData).map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
If you want to skip already fetched cities you can do something like this instead:
useEffect(() => {
displayWeather.map(async city=>{
if (!fetchData[city]) {
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
}
})

Why my hook state doesn´t update correctly?

i don't know how make this guys, i can't update my state with the api array, and if i put it in useEffect i have an error cause i am not sending any data, help me please is my first time using stackoverflow
import React, { useEffect, useState } from "react";
import getTeam from "../Helpers/getTeam";
const selectTeams = [
"Barcelona",
"Real Madrid",
"Juventus",
"Milan",
"Liverpool",
"Arsenal",
];
const Select = () => {
const [team, setTeam] = useState(null);
const [loading, setLoading] = useState(null);
const handleOption = async (e) => {
setLoading(true);
let teamsJson = await getTeam(e.target.value);
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
console.log(arr);
console.log(team);
setTeam(arr);
setLoading(false);
};
return (
<div
style={{ background: "skyblue", textAlign: "center", padding: "20px" }}
>
<h1>Equipos Disponibles</h1>
<div>
<select onChange={handleOption}>
<option>Elige tu equipo</option>
{selectTeams.map((selectTeam, i) => {
return <option key={i}>{selectTeam}</option>;
})}
</select>
</div>
{loading ? <h1>suave</h1> : (
team !== null ? (
team.map((newTeam, i) => {
return (
<div>
the items are here
</div>
)
})
) : null
)}
</div>
);
};
export default Select;
i let you my api file down
const getTeam = async (teamName) => {
const url = `https://www.thesportsdb.com/api/v1/json/1/searchteams.php?t=${teamName}`;
const res = await fetch(url);
const team = await res.json();
return team;
};
export default getTeam;
i wanna update my const team with the response of my api call, but it doesn't update it, i dont know what do, please help me
The teamsJson value is an object with a single key and value of some array
{ teams: [...] }
So you are updating your state with a nested array when you push the value into another array.
let arr = [];
Object.keys(teamsJson).map((teamjs, i) => {
return arr.push(teamsJson[teamjs]);
});
Based upon how you want to map your team state array I assume you just want the raw inner array from teamJson.
const { teams } = await getTeam(e.target.value);
setTeam(teams);
Then when you are mapping you can access any of the properties you need.
team.map((newTeam, i) => {
return <div key={i}>{newTeam.idTeam}</div>;
})
I've just tested it & it seems to works just fine.
The only 2 issues seem to be that:
You don't use team anywhere (apart from a console.log statement).
At the moment when you console.log(team); the constant team will (yet) be null for the first time (because it still keeps the initial state).
Here's what I see in React dev tools after picking a random team in the <select>:

Can't Update State After Deleting Item from Backend with Axios and React

Working on a practice phonebook project where the visitor can enter a name and phone number. Utilizing json-server for the backend and React for front end.
The full code is here Phonebook Github Code
The functionality of adding a number works fine, but I'm having issues with a button which allows the visitor to delete a number. When a user clicks on the 'delete' button, it is successfully removed from the backend (file is db.json). However on the frontend, the deleted number isn't removed, and I can see that the state isn't changing.
Any help is appreciated.
Here's my delete function for removing the number from backend
const deletePerson = id => {
const request = axios.delete(baseUrl + `/` + id);
return request.then(response => response.data);
};
and this function is being called from a button onClick method
const deleteNum = event => {
let personID = event.target.value;
if (window.confirm("Do you really want to delete?")) {
personService
.deletePerson(personID)
.then(() => {
setPersons(persons.filter(item => item.id !== personID));
})
.catch(error => {
console.log("Error", error);
});
}
};
and the rest of the relevant code to give this context
const App = () => {
const [persons, setPersons] = useState([]);
const [newName, setNewName] = useState("");
const [newNumber, setNewNumber] = useState("");
const [filter, setFiltered] = useState("");
useEffect(() => {
personService.getAll().then(initialPersons => setPersons(initialPersons));
}, []);
console.log("Persons", persons);
const peopleToShow =
filter === ""
? persons
: persons.filter(person =>
person.name.toLowerCase().includes(filter.toLowerCase())
);
const rows = () =>
peopleToShow.map(p => (
<p key={p.name}>
{p.name} {p.number}{" "}
<span>
<button value={p.id} onClick={deleteNum}>
delete
</button>
</span>
</p>
));
item.id is stored as a number, whereas the personID is taken as a string. Hence, try changing !== to !=.

Resources