React Firebase getDocs & useEffect makes two calls [duplicate] - javascript

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

Related

React component re-rendering many times

i have a react component thats keep re-rendering idk why but i think the reason is the data fetching
data code :
export function KPI_Stock_Utilisation() {
const [kpi_stock_utilisation, setKpi_stock_utilisation] = useState([{}]);
useEffect(() => {
axios.get("http://localhost:5137/KPI_Stock_Utilisation").then((response) => {
setKpi_stock_utilisation((existingData) => {
return response.data;
});
});
}, []);
console.log('data get')
return kpi_stock_utilisation;
}
this log displayed many times , and the log in the component too
component code :
import React from "react";
import { KPI_Stock_Utilisation } from "../../Data/data";
import { useEffect } from "react";
export default function WarehouseUtilisChart(props) {
let kpi_stock_utilisations =KPI_Stock_Utilisation();
let Stock_utilisation = (kpi_stock_utilisations.length / 402) * 100;
console.log('component render')
return (
<div>
<p>{kpi_stock_utilisations}</p>
</div>
);
}
im new with react i tried useEffect inside the componenets but its not working
Calling the react custom hook KPI_Stock_Utilisation several times will for sure render more than once.
in your case I suggest you use useEffect in the same component as I will show you.
import React,{useEffect,useRef} from "react";
import { KPI_Stock_Utilisation } from "../../Data/data";
import axios from 'axios';
export default function WarehouseUtilisChart(props) {
const [kpi_stock_utilisation, setKpi_stock_utilisation] = useState([{}]);
const stock_utilisation= useRef(0);
useEffect(() => {
axios.get("http://localhost:5137/KPI_Stock_Utilisation").then((response) => {
stock_utilisation.current = (response.data.length / 402) * 100;
setKpi_stock_utilisation(response.data);
});
//this will guarantee that the api will be called only once
}, []);
//you should see this twice, one with the value 0, and another one, the calculated data
console.log('component render',stock_utilisation.current)
return (
<div>
<p>{kpi_stock_utilisations}</p>
</div>
);
}
To note, if you call this component from more than one location, for sure it will render several times - keep that in mind.
On the other hand, all your variables should always start with a lower case and try to name your variables like this: instead of kpi_stock_utilisation change it to kpiStockUtilisation for a better coding practice
You got into infinite loop.
Its hard to explain why it doesn't work as expected, but I can try.
First of all, useEffect with empty array of dependencies works like componentDidMount and fires only after (!) first render.
So you have some value returned from your let kpi_stock_utilisations =KPI_Stock_Utilisation(); then it rendered, after this your useEffect fires a request and set state, setting of state trigger re-render and new value to return, this new value trigger your parent component to return let kpi_stock_utilisations =KPI_Stock_Utilisation(); might run again.
If you are trying to create a custom hook for fetching some info, follow rules of hooks
I hope it helped you

Why does my React app disappear when I run it

Whenever I get data on my page, after a few seconds, my whole react app disappears as in the root div in the html is left completely empty like this <div id="root"></div> as if there is nothing. This is happening on all my other projects too even when I create a new one, this disappearing of the react keeps happening sometimes even without adding any logic, it refuses to render plain html. The errors I get for now on this current project on the console is this
characters.map is not a function
I know not what could be causing this but my code looks like this for now starting with the App.js file. I am extracting data from an api.
import {BrowserRouter, Route, Routes} from "react-router-dom"
import Home from "./components/Home";
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
And then followed by the CharactersListing page which is supposed to render all the characters of the show
import React, {useEffect, useState} from 'react'
import CharacterCard from './CharacterCard'
export default function BadListings() {
const [characters, setCharacters] = useState([])
useEffect(() => {
const getData = async () => {
await fetch("https://www.breakingbadapi.com/api/characters")
.then(response => {
setCharacters(response.json());
console.log(characters);
})
.catch(err => console.log("There must have been an error somewhere in your code", err.message));
}
getData();
});
return (
<div className='container'>
{characters.map(character => (
<div>
<CharacterCard name={character.name} status={character.status} image={character.img} />
</div>
))}
</div>
)
}
And finally, the CharacterCard.js
import React from 'react'
import "../styles/styles.css"
export default function CharacterCard({name, status, image}) {
return (
<div className='card'>
<h1>{name}</h1>
<h2>{status}</h2>
<img src={image} alt="umfanekiso" className='imgur' />
</div>
)
}
I do not know what could be causing this. I have never had this issue it just started today. What could be causing it
Issues
The issue is that you are not setting the characters state to what you think it is. response.json() returns a Promise object and doesn't have a map property that is a function to be called.
The useEffect hook is also missing a dependency, so anything that triggers this BadListings component to rerender will also retrigger this useEffect hook, which updates state and triggers another rerender. The code is likely render looping.
Solution
The code should wait for the response.json() Promise to resolve and pass that result value into the characters state updater function. Note that I've also rewritten the logic to use async/await with try/catch as it is generally considered anti-pattern to mix async/await with Promise chains.
Add a dependency array to the useEffect hook. Since I don't see any dependencies use an empty array so the effect runs only once when the component mounts.
Promise chain Example:
useEffect(() => {
fetch("https://www.breakingbadapi.com/api/characters")
.then(response => response.json()) // <-- wait for Promise to resolve
.then(characters => setCharacters(characters)
.catch(err => {
console.log("There must have been an error somewhere in your code", err.message)
});
}, []); // <-- add empty dependency array
async/await Example:
useEffect(() => {
const getData = async () => {
try {
const response = await fetch("https://www.breakingbadapi.com/api/characters");
const characters = await response.json(); // <-- wait for Promise to resolve
setCharacters(characters);
} catch(err) {
console.log("There must have been an error somewhere in your code", err?.message);
};
}
getData();
}, []); // <-- add empty dependency array
Don't forget to add a React key to the mapped characters:
{characters.map((character) => (
<div key={character.char_id}> // <-- Add React key to outer element
<CharacterCard
name={character.name}
status={character.status}
image={character.img}
/>
</div>
))}
characters is a string and strings don't have .map() method, that's why React is crashing. And since React's crashed, it couldn't mount generated HTML to the #root.
You can use [...strings] to use .map() method.
You are exporting CharacterCards and not BadCards.
Please change all BadCards in your CharactersListing page to CharacterCards
There is an explaination What does "export default" do in JSX?
Great instinct to look for errors in the console.
Umut Gerçek's answer is correct, but I'd add an additional suggestion: if you're going to map over something, you should instantiate it in state as a thing that can be mapped. Set its initial state to an array:
const [characters, setCharacters] = useState([])
Note the capital 'C' in the setter; that is the useState convention.
Then set characters as you're currently doing:
setCharacters(response.json());
And your map should work regardless of the result of your fetch, and handle multiple items, too.

Why am I unable to use Okta hooks inside useEffect? (React)

Recently I posted a question in which I was having some conditional rendering issues with a React/Okta project using TypeScript (for context):
Am I unable to use inline "if" with useEffect in a functional React component?
I've since solved that issue but now I'm experiencing another issue within the following component:
import React, { useState, useEffect } from 'react';
import { useOktaAuth } from '#okta/okta-react';
import { Dropdown } from 'semantic-ui-react';
import { UserInfo } from '../../interfaces/UserInfo';
export const HeaderUserMenu = () => {
// Okta and State hooks
const { authState, oktaAuth } = useOktaAuth();
const [ userInfo, setUserInfo ] = useState<UserInfo>({});
// Log current user data
const logUser = () => {
console.log(userInfo);
}
// Watch the auth state and user info for changes, update state.
useEffect(() => {
if (!authState || !authState.isAuthenticated) {
setUserInfo({});
} else {
oktaAuth.getUser((info: UserInfo) => {
setUserInfo(info);
})
}
}, [authState, oktaAuth]);
// If we have an empty user object, return nothing, otherwise...
if (Object.keys(userInfo).length === 0) {
return null;
} else {
return (
<Dropdown item text={userInfo.name}>
<Dropdown.Item onClick={logUser}>Log User</Dropdown.Item>
</Dropdown>
)
}
}
When I successfully log into Okta, authState and oktaAuth in the useOktaAuth() hook should have stuff in them, and via useState and the useEffect hook, I should be able to fetch the current user's info.
However, it's seeming as if authState and oktaAuth from the hook aren't making it into the effect being run, so even though I'm currently logged into the app, this component is not being rendered at all.
Is it not possible to use hooks like this inside of an effect? The documentation uses this exact pattern so I would have assumed this would be proper usage of useOktaAuth() and the Effect hook, but I can't get it working.
Also, one of the things I had to do to fix my previous issue was the addition of checking for !authState at the beginning of the effect, otherwise it -always- returns an error of:
TypeError: Cannot read property 'isAuthenticated' of null
So I'm not sure if this has something to do with it? There's NO reason that authState or oktaAuth should be null, unless the hook just isn't working? Or there's some issue in combining that hook with useEffect.
Any thoughts?

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

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

Categories

Resources