useEffect() in Reactjs version #17.0.1 [duplicate] - javascript

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>;
})}

Related

React Firebase getDocs & useEffect makes two calls [duplicate]

This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed last month.
I'm trying to get data from firebase in react, using useEffect to avoid creating loops.
My code works so far, but I get the results twice. When I tried to find out what the problem was, I found that the data was also retrieved twice. Because the whole code section is executed twice.
--> i get the "Did request!" from console.log("Did request!") 2x times
import React, { useEffect, useState } from "react";
import { db } from "../firebase-config";
import { collection, getDocs } from "firebase/firestore";
function MusicList() {
const [musicList, setMusicList] = useState([]);
const getData = async () => {
try {
const querySnapshot = await getDocs(collection(db, "music"));
querySnapshot.forEach((doc) => {
setMusicList((oldArray) => [...oldArray, doc.data()]);
});
console.log("Did request!");
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getData();
}, []);
return (
<div className="MusicList">
{musicList.map((music) => {
return <div key={music.id}>{music.songName}</div>;
})}
</div>
);
}
export default MusicList;
Being relatively new to React and the concept of "useEffect" I don't know exactly why this is happening.
This is most likely because you have React strict mode enabled? It does this very annoying thing where it renders components twice. Remove it and it should only render once. Let me know if it works:
<StrictMode> <<--- remove this
<App />
</StrictMode> <<--- remove this
UPDATE: I should mention this is a quick fix. The double rendering is done to ensure you've put in features such as query caching etc where bugs can be detected by double rendering.
Further information: https://github.com/facebook/react/issues/24502#issuecomment-1118754581

TypeError : post.map() is not a function [duplicate]

This question already has answers here:
React does not wait for server call to complete and throws error
(3 answers)
An error comes when mapping array of objects in React
(3 answers)
Closed 1 year ago.
It will continuously showing this error that TypeError: post.map is not a function.
please give me some appropriate answer guyz.
import React from "react";
import { useState } from "react";
function LoopThrough() {
const [post, setpost] = useState("");
function getDataFromApi() {
// using axios method
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((get) => {
console.log(get.data);
setpost(get.data);
})
.catch((error) => {
console.log(error);
});
}
const getdatafromjson = post.map((myobj) => {
return (
<div>
<h2>{myobj.title}</h2>
</div>
);
});
return (
<>
<h2>Return Html using http methods</h2>
<button onClick={getDataFromApi}>Get Data</button>
{getdatafromjson}
</>
);
}
export default LoopThrough;
Note : In this example , i want to get data from external Api using Axios method(language: React JS) and render it onto the display using react hooks and map() method but it doesnt work.
Error :
TypeError: post.map() is not a function .

How does useState set variables? [duplicate]

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

Problem with custom ContextProvider state when using custom hook for fetching data in multiple tables in React

I have multiple tables, of small size, and I want to be able to write / read / update my components when the corresponding table has been updated by the app (we can consider it's a single user app for the moment).
I've been inspired by this question to write a custom Provider and associated hook for data fetching (and eventually posting) in my app: React useReducer async data fetch
I came up with this:
import React from "react";
import { useContext, useState, useEffect } from "react";
import axios from "axios";
const MetadataContext = React.createContext();
function MetadataContextProvider(props) {
let [metadata, setMetadata] = useState({});
async function loadMetadata(url) {
let response = await axios.get(url);
// here when I console.log the value of metadata I get {} all the time
setMetadata({ ...metadata, [url]: response.data });
}
async function postNewItem(url, payload) {
await axios.post(url, payload);
let response = await axios.get(url);
setMetadata({ ...metadata, [url]: response.data });
}
return (
<MetadataContext.Provider value={{ metadata, loadMetadata, postNewItem }}>
{props.children}
</MetadataContext.Provider>
);
}
function useMetadataTable(url) {
// this hook's goal is to allow loading data in the context provider
// when required by some component
const context = useContext(MetadataContext);
useEffect(() => {
context.loadMetadata(url);
}, []);
return [
context.metadata[url],
() => context.loadMetadata(url),
(payload) => context.postNewItem(url, payload),
];
}
function TestComponent({ url }) {
const [metadata, loadMetadata, postNewItem] = useMetadataTable(url);
// not using loadMetadata and postNewItem here
return (
<>
<p> {JSON.stringify(metadata)} </p>
</>
);
}
function App() {
return (
<MetadataContextProvider>
<TestComponent url="/api/capteur" />
<br />
<TestComponent url="/api/observation" />
</MetadataContextProvider>
);
}
export default App;
(the code should run in CRA context, both apis can be replaced with almost any API)
When I run it, a request is fired on both endpoints (/api/capteur and /api/observation), but where I'm expecting the metadata object in the MetadataContextProvider to have 2 keys: "/api/capteur" and "/api/observation", only the content of the last request made appears.
When I console.log metadata in the loadMetadata function, metadata always has the initial state hook value, that is {}.
I'm fairly new to React, I tried hard and I'm really not figuring out what's going on here. Can anyone help?
Your problem is how you update the metadata object with setMetadata.
The operation of updating the metadata object via loadMetadata in your context is done by two "instances" respectively: TestComponent #1 and TestComponent #2.
They both have access to the metadata object in your context, but they're not instantly synchronized, as useState's setter function works asynchronously.
The easy solution for your problem is called functional updates.
useState's setter does also provide a callback function, which will then use (I'm oversimplifying here) the "latest" state.
In your context provider:
async function loadMetadata(url) {
let response = await axios.get(url);
setMetadata((existingData) => ({ ...existingData, [url]: response.data }));
// instead of
// setMetadata({ ...metadata, [url]: response.data });
}
Here is a working CodeSandbox: https://codesandbox.io/s/elegant-mclean-syiol?file=/src/App.js
Look at the console to see the order of execution.
I highly recommend to fully read React hooks documentation, especially the "Hooks API Reference". There are also other problems with your code (for example missing dependencies in the useEffect hook, do you have ESLint enabled?).
If you want to have a better overview on how to use React's context I can recommend Kent C. Dodds' blog:
https://kentcdodds.com/blog/application-state-management-with-react
https://kentcdodds.com/blog/how-to-use-react-context-effectively

React Hooks & UseEffect not updating display with socketIO data. Only renders elements in array

import React, {useState, useEffect} from 'react';
import socketIO from 'socket.io-client';
import Container from 'react-bootstrap/Container';
function Sock() {
const [textData, setTextData] = useState([]);
useEffect(() => {
const socket = socketIO('http://127.0.0.1:5009');
socket.on('chat message', (text) => {
setTextData([
textData.push(text)
]);
console.log(textData);
});
},[]);
return (
<Container>
<h1>Socket</h1>
{textData.map((text) => <li>{text}</li>)}
</Container>
);
}
export default Sock;
With help I have managed to display something on the UI but it currently only displays the array count and not the object inside the array. I am fairly new to React hooks any help or suggestions would be greatly appreciated.
There are few ways to take care of stale closure but for your simple case, you can use the functional update syntax to get the previous text data, and return a new instance of array (do not mutate existing state, using push).
useEffect(() => {
const socket = socketIO("http://127.0.0.1:5009");
socket.on("chat message", text => {
setTextData(previousTextData => [...previousTextData, text]);
});
}, []);
Using the callback approach, you don't need to put textData in the useEffect's dependency array.
You have to join your data (text) with existing data (textData), try with:
setTextData([...textData, text]);

Categories

Resources