How does useState set variables? [duplicate] - javascript

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I've posted something similar in the past and never really got an answer and I keep running into this problem. Things in my app are working, but I'm not really sure why. I'm using a third-party api to make a simple recipe app. The data I'm passing into my component is displaying as expected, but it's not logging into the console as expected.
Line 20 of App.js is logging what I'd expect (the data received from the api).
Line 4 of Recipe.jsx is logging what I'd expect (each item in the recipes array).
But line 22 of App.js is coming back as undefined. I would expect that because I setRecipes to 'data', that 'recipes' would log the same as 'data'. Can anyone explain this to me?
import React, { useEffect, useState } from "react";
import "./App.css";
import Recipe from "./Recipe";
import axios from "axios";
function App() {
const APP_ID = "XXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}`;
const [recipes, setRecipes] = useState([]);
useEffect(() => {
getRecipes();
}, []);
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
console.log(recipes);
};
return (
<div className="App">
<form className="search-form">
<input className="search-input" type="text" />
<button className="search-button" type="submit">
Search Recipes
</button>
</form>
{recipes &&
recipes.map((recipe, idX) => <Recipe recipe={recipe} id={idX} />)}
</div>
);
}
export default App;
import React from "react";
const Recipe = ({ recipe, id }) => {
console.log(recipe);
return (
<div key={id}>
<h1>{recipe.recipe.label}</h1>
<p>{recipe.recipe.calories}</p>
<img src={recipe.recipe.images.SMALL.url} alt="" />
</div>
);
};
export default Recipe;

SetState is asynchronous and may not resolve straightaway. See this part of the docs for more information
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
As you've said, the data coming back is correct, and you've made a call to set state. But the state hasn't resolved by the time you come to the next line where state is consoled out

Related

unable to map as url is not an array // React Router

Hi I have a main page called FeaturedProduct.js which lists all the products fetch from the API https://fakestoreapi.com.
I trying to set up react router dom version (6) whereby user click on any of the product will open up that single product through Product.js
This is my code: https://codesandbox.io/s/magical-smoke-r7yik9?file=/src/Product.js
I having issues because I can't use the map function without errors.
The error being `data.map' is a not a function (in Product.js)
Do I need to access further into the "api" json like data.x.map?
Always check carefully the response you are getting from an api weather its object or an array.
i have modified your file, you can replace it with your original product.js component.
And always use ?. operator whenever you play with data that come from an api because useEffect hook will run after your component is loaded, hence it will give you an error that your data is undefined. It will not happen in your case you are getting only single object which is fast operation, but if you are getting larger data then you have to use ?. operator.
import React, { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import axios from "axios";
// individual product
// functional component
const Product = () => {
const [data, setData] = useState('');
const { id } = useParams();
useEffect(() => {
axios
.get(`https://fakestoreapi.com/products/${id}`) // change from ?id=${id} to ${id} because API url is .com/products/1 // But couldn't map due to not being array
.then((res) => {
setData(res.data);
})
.catch((err) => console.log(err));
}, []);
return (
<div>
<div className="product-container" key={data?.id}>
<div>
<img className="prod-image" src={data?.image} alt="" />
</div>
<div>
<h1 className="brand">{data?.title}</h1>
<h2>{data?.category}</h2>
<p>{data?.description}</p>
<p>
<strong>Price:</strong> {data?.price}
</p>
<p>
<strong>Rating:</strong> {data?.rating?.rate}
</p>
</div>
</div>
<div className="back">
<Link to="/">Feature Products</Link>
</div>
</div>
);
};
export default Product;

Why won't the useEffect hook run and fetch data?

I am creating this simple news app with dotnet webapi and react. I want to fetch the news from my backend. The API is working fine, I have tested it, but somehow my useEffect won't run and I can't fetch the data. What am I doing wrong and what is the proper way of doing this? Any help will be appreciated!
Here's my app.js where the fetching should be working.
import './App.css';
import axios from 'axios';
import ArticleList from './/components/ArticleList.jsx';
function App() {
const [articles, setArticles] = useState(null)
useEffect(() => {
console.log('If it works, this should be shown!')
axios.get('https://localhost:7112/News').then(response => {
setArticles(response.data)
console.log(articles)
});
},[]);
return (
<div className="App">
<ArticleList articles={articles}/>
</div>
);
}
export default App;
import ArticleListItem from './ArticleListItem.jsx'
export default function ArticleList(articles) {
return (
<>
{articles && articles.map(article => (<ArticleListItem key={article.title} article={article} />
))}
</>
)
}
There's the component which throws error: articles.map is not a function.
The error does not come from useEffect neither tha App component . But ArticleList it self when you pass articles as a props to ArticleList component and before mapping it you have to check if you have articles first :
You can do something like this :
{props.articles && props.articles.map(...)
You need to check articles to render Loading or ArticleList
{articles && <ArticleList articles={articles}/>}
or you set initial value for articles state:
const [articles, setArticles] = useState([])
In my case, I was using webstorm and for some reason it was not printing the useEffect console logs in my webstorm terminal, when I checked the logs in the browser console everything was getting printed in the logs

React shows the answer options the first time, and then when I reload I get an error

I'm currently trying to make an quiz in React app and this is the code:
import React from "react";
import {useState, useEffect} from "react"
import { nanoid } from 'nanoid'
// Here is the function that gets new questions from an API
function App(){
const [allQuestions, setAllQuestions] = useState([])
const [currQuestion, setCurrQuestion] = useState(0)
function newQuiz() {
fetch('https://opentdb.com/api.php?amount=5&category=12&difficulty=easy&type=multiple')
.then(res => res.json())
.then(data =>
setAllQuestions(data.results.map((item) => {
return{
id: nanoid(),
question: item.question,
correctAnswer: item.correct_answer,
allAnswers: [...item.incorrect_answers, item.correct_answer]
}
})))
}
useEffect(() => {
newQuiz();
}, [])
return(
<div>
// here, I render the answer options to the screen
{allQuestions[currQuestion].allAnswers.map(quest => <button>{quest}</button> )}
</div>
)
}
export default App
So, after I save, everything works just fine, and after I reload I get this error in console:
"Uncaught TypeError: Cannot read properties of undefined (reading 'map')".
Thanks in advance.
You must add safe checks before trying to read something from array, because list will be empty until you resolve data inside useEffect, and when trying to read on an element of empty list, you will end up with error like that one. Add checks using ? (or add ternary check with allQuestions.length > 0), like this:
{allQuestions[currQuestion]?.allAnswers?.map(quest => <button>{quest}</button> )}

Getting an Empty array from the firestore

This is my code for LeftBar Component
I want to get the data stored in the "contacts" document in the firebase but getting an empty array. Idk know why this is happening. And one thing if anyone can tell me how to do it with class-based component ( how to use that use effect thing into ComponentDidMount and ComponentDidUpdate) as I have two other class Components which are also using the same functionality Please help
import React, {useState , useEffect} from "react";
import { Avatar, IconButton } from "#material-ui/core";
import AddCircleIcon from "#material-ui/icons/AddCircle";
import MoreIcon from "#material-ui/icons/More";
import ChatBubbleOutlineIcon from "#material-ui/icons/ChatBubbleOutline";
import SearchIcon from '#material-ui/icons/Search';
import LeftChats from './LeftChats';
import "./LeftBar.css";
import db from './firebase'
function LeftBar () {
const [contacts, setContacts] = useState([])
useEffect(() => {
db.collection("contacts").onSnapshot((snapshot)=> setContacts(
snapshot.docs.map((doc)=> ({
id : doc.id,
data : doc.data(),
}))
))
},[])
console.log(contacts);
return (
<div className="leftbar">
<div className="left-header">
<Avatar />
<div className="left-right-header">
<IconButton>
<AddCircleIcon />
</IconButton>
<IconButton>
<MoreIcon />
</IconButton>
<IconButton>
<ChatBubbleOutlineIcon />
</IconButton>
</div>
</div>
<div className="left-search">
<div className='input-container'>
<SearchIcon/>
<input type='text' placeholder='Search...'/>
</div>
</div>
<div className="left-chats">
{
contacts.map( contact =>(
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
))
}
</div>
</div>
);
}
export default LeftBar;
This is my LeftChat Component
import React, { Component } from 'react'
import { Avatar} from "#material-ui/core";
class LeftChats extends Component {
constructor(props) {
super(props)
this.state = {
data : ''
}
}
render()
{
console.log(this.props)
return (
<div className='leftchats'>
<Avatar/>
<div className='chats-info'>
<h2>{this.props.username}</h2>
<p>Some text message...</p>
</div>
</div>
)
}
}
export default LeftChats
Using
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
means that you are passing {contact.data.name} value to your custom LeftChats component. But your data prop is empty. Check your console to see whether you get data or not, since you've used console.log(this.props) inside <LeftChats />.
And for the sake of completness, you should import all from firebase, unless you know exactly what you'are doing:
import * as firebase from 'firebase';
import 'firebase/firestore';
Then have these configuration set appropriately:
const firebaseConfig = {
apiKey: "AIsdfghjzaSyC0ZHz8ooSoi05Vt2X7UL7qV9Aga1o", // use your own
authDomain: "dj-himmels.firebaseapp.com",
databaseURL: "https://dj-himmels.firebaseio.com",
projectId: "dj.himmels",
storageBucket: "dj-himmels.appspot.com",
messagingSenderId: "285437504174",
appId: "1:285566504174:web:0a9ttgb4a968acd08f7ff"
};
which is missing from your code.
In a real react native app for production, you should be using AsyncStorage to proceed your data.
Note: For your next questions try to ask one question at a time, it helps to figure out your real need and answer it accordingly. Right now you seem to be having many issues.
EDITED AFTER THE POSTER COMMENT
1- First of all, you are passing {contact.data.name} to username instead of {contacts.data.name}, because you used React hook useState to define contacts as const [contacts, setContacts] = useState([]) and not as const [contact, setContact] = useState([])
2- Second of all, if at all the
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
component found the {contacts.data.name}, value to pass to username= contacts.data.name}, you will still get empty result because using useState([]) inside const [contacts, setContacts] = useState([]) means that you have set Contact value to empty array.
Nevertheless, you have tried to send value to Contact using React
useEffect hook like so useEffect(() => {
db.collection("contacts").onSnapshot((snapshot)=> setContacts(
snapshot.docs.map((doc)=> ({
id : doc.id,
data : doc.data(),
}))
))
},[]),
which was not triggered before component receives the initial empty array of contact. And your data value fetched from firebase may also be empty, that's why I meant by you are having many issues in the same time.
MY SUGGESTION:
1- Console.log the doc value to check whether it is empty or not. If so, then the problem is in the snapshot.docs.map((doc) otherwise it may be in the empty data inside the
this.state = {
data : ''
}
I make suggestions because I don't really know your real architecture. Also take a look at the react useEffect hooks here You may need to set a conditional value inside the [] of your
useEffect(() => {
},[x])
to reset Contact after that condition. Ultimately remember that there is still a great amount of value in sticking with functional components rather than going back to the class based components. Hope it help !

useEffect() in Reactjs version #17.0.1 [duplicate]

This question already has answers here:
JavaScript: Difference between .forEach() and .map()
(17 answers)
Closed 24 days ago.
I was working on a MERN app. In a component i need to request a api. I used axios and used it in useEffect & then i used setState to set state to the response i get from api. But the state is not updating.
I saw some youtube tutorials but they are doing the same code & getting the state updated. Is this because of new version of react #17.0.1 which i am using.
At last how do i solve this problem.....? need help.
posts.js file inside react app-
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(async () => {
const res = await axios.get('http://localhost:5000/posts');
console.log(res.data); // res.data is a array of objects
setPosts(res.data); // this is not working...
}, []);
return (
<div>
{posts.forEach((post) => {
<ul>
<li>{post.username}</li>
<li>{post.content}</li>
</ul>;
})}
</div>
);
};
export default Posts;
I hope you understand the basic principle of using map method here,
Generally speaking (for each) doesn't return anything. Meaning that Data from looping the original array won't reusable after the loop
But map method return another array
You can read here Map vs foreach
So what you doing now is
{posts.forEach((post) => {
<ul>
<li>{post.username}</li>
<li>{post.content}</li>
</ul>;
})}
This syntax is actually looping the undefined . Which will return nothing.
if you wanna loop
you need to use map method
{posts.map((post) => {
return <div>.....</div>;
})}

Categories

Resources