infinite loop using useEffect()? - reactjs

hi im getting an infinite loop with useEffect.. it seems it is not displaying any children prop instead it loops on an empty prop.
import {useEffect , createContext , useState} from "react"
import {auth} from '../../firebase'
export const AuthContext = createContext()
export const AuthContextProvider = ({children}) =>{
const [currentUser , setCurrentUser] = useState({})
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth ,(user) => {
setCurrentUser(user)
})
return ()=>{
unsubscribe()
}
}, []);
return(
<AuthContext.Provider value={{currentUser}}>
{children}
</AuthContext.Provider>
)
}
P.S this is the only code that use useEffect
Error:
react-dom.development.js:86 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

Related

how useContext hook is working here with useState hook

import { createContext, useState } from "react";
this is the place where the actual useContext data is going to be saved currentUser and setCurrentUser
export const UserContext = createContext({
currentUser: null,
seCurrentUser: () => null,
});
but here is userProvider with useState Hook and what is
the function of this useState Hook here and how Value is adding data in useState hook, if it is?
export const UserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const value = { currentUser, setCurrentUser };
return (
<UserContext.Provider value={value}> {children} </UserContext.Provider>
);
};
please tell me how it's going to be used in other web pages to collect data it's really confusing for me i'm trying to understand since last week.
this is the place where the actual useContext data is going to be saved currentUser and setCurrentUser
export const UserContext = createContext({
currentUser: null,
seCurrentUser: () => null,
});
Actually, this is just the default value. You will not be storing data here, and if a component ever gets these values it means you have forgotten to render a <UserContext.Provider>.
const [currentUser, setCurrentUser] = useState(null);
const value = { currentUser, setCurrentUser };
return (
<UserContext.Provider value={value}> {children} </UserContext.Provider>
);
This is where the work really happens. Your component has a state, which behaves just like any other state in react. The only thing different is that you are then making the current user and the setCurrentUser function available via context, so components farther down the tree can use and change the state.
Context is just a way to pass a value Component A to Component B. You still need to actually implement state or other code to do what you want.
Here's how it looks to consume the context:
const SomeComponent = () => {
const { currentUser, setCurrentUser } = useContext(UserContext);
// currentUser is the state found in UserProvider, and setCurrentUser
// is the state setter function, also from UserProvider
}

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array

this is a custom hook that allows the user to log in dashboard page if he loggedin
import { getAuth, onAuthStateChanged } from 'firebase/auth'
import { useState, useEffect } from 'react'
export function useAuthStatus() {
const [loggedIn, setLoggedIn] = useState(false)
const [checkingStatus, setCheckingStatus] = useState(true)
useEffect(()=>{
const auth = getAuth()
onAuthStateChanged(auth , (user)=>{
if(user){
setLoggedIn(true)
}
setCheckingStatus(false)
})
console.log("hello")
}, [])
return {loggedIn, checkingStatus}
}
and here where im use it as a private route
import {useAuthStatus} from '../hooks/useAuthStatus'
import { Outlet , Navigate} from 'react-router-dom'
function PrivateRoute() {
const {loggedIn, checkingStatus} = useAuthStatus();
if(checkingStatus){
return <div>looding...</div>
}
return loggedIn ? <Outlet/> : <Navigate to="/auth/sign-in"/>
}
export default PrivateRoute
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array

Rendered more hooks than during the previous render when using hooks in subcomponent

I am trying to understand how I can use React Hooks in subcomponents without triggering an error.
Let's say I have a super simple component (all the code is runnable here - https://codesandbox.io/embed/xenodochial-wright-mwqlk?fontsize=14&hidenavigation=1&theme=dark):
import { useEffect, useState } from "react";
const TestComponent = (index) => {
const [a, setA] = useState("nothing");
useEffect(() => {
setA(index);
}, [setA, index]);
return <>{a}</>;
};
export default TestComponent;
And it is used like this:
import { useEffect, useState } from "react";
import Component from "./Component";
export default function App() {
const [arr, setArr] = useState([]);
useEffect(() => {
setArr([1]);
}, []);
const component = arr.map((_, index) => Component(index));
return <div>{component}</div>;
}
Now I am getting an error:
Error
Rendered more hooks than during the previous render.
▶ 5 stack frames were collapsed.
TestComponent
/src/Component.js:4:29
1 | import { useEffect, useState } from "react";
2 |
3 | const TestComponent = (index) => {
> 4 | const [a, setA] = useState("nothing");
| ^
5 |
6 | useEffect(() => {
7 | setA(index);
There are more details in the console:
Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
Previous render Next render
------------------------------------------------------
1. useState useState
2. useEffect useEffect
3. undefined useState
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at App (https://mwqlk.csb.app/src/App.js:24:41)
I understand the basic concept of the hooks rules that I cannot use hooks after if statements or such, they should always be on the top level.
However, this case is clearly not using any conditionals for declaring the hooks. All additional hooks are actually in a subcomponent. So why are they accounted for by React as part of the App component?
So, how can I safely use hooks in imported subcomponents then?
You're calling your react component like a function instead of using it as a component. This causes some weirdness with rendering. If you change your .map line to
const component = arr.map((_, index) => <Component index={index} />);
and destructure your props appropriately in Component
const TestComponent = ({ index }) => {
everything works as expected.
Here's how you should pass
App.js:
import { useEffect, useState } from "react";
import Component from "./Component";
export default function App() {
const [arr, setArr] = useState([]);
useEffect(() => {
setArr([10,12,12,12,12]);
}, []);
return <>{arr.map((_, index) => (
<Component key={index} index={index}/>
))}</>
}
Test component:
import { useEffect, useState } from "react";
const TestComponent = ({index}) => {
const [a, setA] = useState("");
useEffect(() => {
setA(index);
}, []);
return <>{a}</>;
};
export default TestComponent;
You should refactor in this way
Component.js
import { useEffect, useState } from "react";
const TestComponent = ({index}) => {
const [a, setA] = useState("nothing");
useEffect(() => {
setA(index);
}, [setA, index]);
return <>{a}</>;
};
export default TestComponent;
App.js
import { useEffect, useState } from "react";
import TestComponent from "./Component";
export default function App() {
const [arr, setArr] = useState([]);
useEffect(() => {
setArr([1]);
}, []);
const component = arr.map((_, index) => <TestComponent index={index}/>);
return <div>{component}</div>;
}

createContext using a dynamic object

1. Static object
To create context based on a static object, I use this code:
import React, { createContext } from 'react';
const user = {uid: '27384nfaskjnb2i4uf'};
const UserContext = createContext(user);
export default UserContext;
This code works fine.
2. Dynamic object
But if I need to create context after fetching data, I use this code:
import React, { createContext } from 'react';
const UserContext = () => {
// Let's suppose I fetched data and got user object
const user = {uid: '18937829FJnfmJjoE'};
// Creating context
const context = createContext(user);
// Returning context
return context;
}
export default UserContext;
Problem
When I debugg option 1, console.log(user) returns the object. Instead, option 2, console.log(user) returns undefined. What I'm missing?
import React, { useEffect, useState, useContext } from 'react';
import UserContext from './UserContext';
const ProjectSelector = (props) => {
const user = useContext(UserContext);
console.log(user);
return(...);
}
export default App;
one thing i would suggest is move this logic to a react component itself.
anhow you need to use a Provider in which you will set value to be the value consumers need to consume.useEffect is greatway to do asynchronous updates, like your requirment.
so , use a state variable as value of provider.in useEffect you fetch the data and update the state variable which in turn will update context value.
following is the code
UserContext.js
import { createContext } from "react";
const UserContext = createContext();
export default UserContext;
App.js
export default function App() {
const [user, setUser] = useState();
useEffect(() => {
console.log("here");
fetch("https://reqres.in/api/users/2")
.then(response => {
return response.json();
})
.then(data => {
setUser(data);
});
}, []);
return (
<div className="App">
<UserContext.Provider value={user}>
<DummyConsumer />
</UserContext.Provider>
</div>
);
}
DummyConsumer.js
import React, { useContext } from "react";
import UserContext from "./UserContext";
const DummyConsumer = () => {
const dataFromContext = useContext(UserContext);
return <div>{JSON.stringify(dataFromContext)}</div>;
};
export default DummyConsumer;
demo:anychronus context value providing

How to write a test for conditional rendering component depended on useState hook in React?

I'm trying to write a test for my functional component, but don't understand how to mock isRoomsLoaded to be true, so I could properly test my UI. How and what do I need to mock?
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchRooms } from '../../store/roomsStore'; // Action creator
// Rooms component
export default ({ match, location, history }) => {
const roomsStore = useSelector(state => state.rooms);
const dispatch = useDispatch();
const [isRoomsLoaded, setRoomsLoaded] = useState(false);
useEffect(() => {
const asyncDispatch = async () => {
await dispatch(fetchRooms());
setRoomsLoaded(true); // When data have been fetched -> render UI
};
asyncDispatch();
}, [dispatch]);
return isRoomsLoaded
? <RoomsList /> // Abstraction for UI that I want to test
: <LoadingSpinner />;
};
If you want, you could flat out mock useState to just return true or false, to get whichever result you want by doing the following.
const mockSetState = jest.fn();
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: value => [true, mockSetState],
}));
By doing this, you're effective mocking react, with react, except useState, its a bit hacky but it'll work.

Resources