react.js hook (too many re-renders) - reactjs

I'm new to react.js hook. Not sure about why the following code doesn't work.
import React,{useEffect,useState} from "react"
function App() {
const [fruit,setFruit] = useState("orange");
setFruit("apple")
return (
<div>
<h1>{fruit}</h1>
</div>
);
}
export default App;
The error says that
Too many re-renders. React limits the number of renders to prevent an infinite loop.

You're setting the state inside the functional component body (The setFruit("apple") line) which will cause the component to re-render and when this happens all the component body will rerun again which will cause another set state call and so on which ultimately causes an infinite loop.
If you want to set the state when the component is mounting, You can use the useEffect hook to achieve that like so:
useEffect(() => {
setFruit("apple")
}, []);
Read more about the hook here.

you need to use useEffect
useEffect(() => {
setFruit("apple")
}, [])

The actual pattren of react is that when you set a state it cause rerender that means component re run again and recompile every line of code if it found a set state fucntion again it rerender and that's what happening in youe code. To prevent this you can use useEffect hook which accepts a callback function and a dependency array
For Example
useEffect(() => {}, []);
The code in every useEffect will run with first time component mount and if you want to run the code again you need to pass dependency in 2nd argument of useEffect which decide that if the dependency change it's value the code in useEffect will run again.
In your case you can set state like this
useEffect(() => {
setFruit("apple")
}, []);

Related

How do I call useEffect when the trigger is in another component?

I am trying to fetch data in a functional React component using useEffect (since the function is asynchronous) and I compute and return the data in h2 tags that was fetched in this component. However, I want to fetch the data when in another component (App.js) I hit the enter key. I have an enter key handler which calls a useState which I believe should re-render the component but since the useEffect is in another component, I am not getting the calling of the useEffect as I intend. What is the best method to press the Enter key and have useEffect in another component run?
function handleKeyDown(event){
if(event.key === 'Enter'){
//this setHeadingText is a useState variable which should re-render in App.js
setHeadingText(name);
}
It sounds like your useEffect is specifying an empty dependency array (instructing to run only once):
useEffect(() => {
//fetch some data
}, []); // This empty array ensures it only runs 1 time
Have a look at this section in the docs: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
If your second component has access to your headingText state, try specifying your headingText as a dependency to the useEffect, and any changes to it should trigger your useEffect
useEffect(() => {
//fetch some data
}, [headingText]); // Only re-run the effect if headingText changes
Alternatively, remove the dependency array from your useEffect.
Note: this will cause the useEffect to run on every re-render
useEffect(() => {
//fetch some data
}); // run every time the component re-renders
Now that I have understood properly, I think this is the solution you wished for:
The headingText value that is being updated in your App.jsx component should be passed down to your Child component with the help of props. So use <Child headingText={headingText} /> while loading the Child component in your App.jsx.
Inside your Child component, receive the value like this function Child(props) or function Child({ headingText }) if you have more values to pass, prefer props.
Now you can easily access the changes in value made in your App component inside your Child component with the help of props.headingText or headingText respective to the way you defined your Child in point 2.
To re-render your Child component, you will now use the useEffect hook with its dependency set to headingText, like:
React.useEffect(() =>
{
// Code for refetching
}
, [headingText]); // or props.headingText if your Child component uses (props)
For example: CodeSandbox
Hope that helps you!
you can make a conditional rendering with the other component, so it get rendered only if you press Enter which would invoke an event:
//App.js
import {AnotherComponent} from './anothercomponent.js' //assuming the file is in src folder
function RenderOnEnter({pressed}){
if(pressed){
return (
<AnotherComponent/>
)
}
return null
}
function App(){
const [pressed,setPressed] = useState(false)
function handlePressed(e){
if(e.target.key === 'Enter'){
setPressed(True)
}
else{
setPressed(False)
}
}
return(
<div>
<button onClick={(e)=>handlePressed(e)}>Click me to obtain data!</button>
<RenderOnPress pressed={pressed}/>
</div>
)
}

Why is useEffect running twice?

import { useContext, useEffect, useState } from 'react';
const Log = () => {
useEffect(() => {
console.log('Running ...')
},[])
return(<p>here</p>)
}
export default Log;
Whenever this code runs, I get
Running... messages twice in the browser console.
I think it should run once, as I have an empty second parameter in useEffect.
Can anybody explain why it is getting run twice?
This is due to <StrictMode> most likely in your root tree.
What is strict mode?
StrictMode is a tool for highlighting potential problems in an application.
How does it make useEffect() run twice?
It activates additional checks and warnings for its descendants, or in other words... renders twice.
Note: Strict mode checks are run in development mode only; they do not impact the production build.
If useEffect doesn't have any dependencies, the component itself is called only once.
This answer will help you.
React Hooks: useEffect() is called twice even if an empty array is used as an argument
As the dependency list of useEffect() sets empty, "console.log" will automatically run whenever the Log component re-renders.
I think there might be some changes of context or parent component from component tree.
Please check your project structure.
<ContextProvider.Provider value={setGlobalState}>
<ParentComponent>
<Log/>
</Parent Component>
</ContextProvider.Provider>
You can prevent running useEffect() twice by using the following way,
import { useEffect, useRef } from 'react';
const Log = () => {
// initiate dataFetch
const dataFetch = useRef(false)
useEffect(() => {
console.log('Running ...')
// start:: prevent executing useEffect twice
if (dataFetch.current)
return
dataFetch.current = true
// end:: prevent executing useEffect twice
}, [])
return (<p>here</p>)
}
export default Log;

React with TypeScript - React has detected a change in the order of Hooks called by ComponentName

I am working with a project with users. Right now I am working with the UserProfile
This is the error I am recieving.
React has detected a change in the order of Hooks called by UserProfile
Previous render Next render
------------------------------------------------------
1. useState useState
2. useContext useContext
3. useEffect useEffect
4. undefined useContext
Let me show some code of the UserProfile component.
export const UserProfile = () => {
document.title = `${title} - My Profile`;
const [profile, setProfile] = useState<UserDetails>();
const {claims} = useContext(AuthContext);
const getUserEmail = (): string => {
return claims.filter(x => x.name === "email")[0]?.value.toString();
}
useEffect(() => {
axios.get(`${urlAuth}?userName=${getUserEmail()}`)
.then((response: AxiosResponse<UserDetails>) => {
setProfile(response.data);
})
}, [getUserEmail]);
return (
profile ?
<article>
<h1>This profile belongs to {UserName()}</h1>
<h2>{profile.name}</h2>
</article>
: <div>Loading...</div>
)
}
I get a warning at the getUserEmail function,
It says
The 'getUserEmail' function makes the dependencies of useEffect Hook (at line 26) change on every render.
Move it inside the useEffect callback.
Alternatively, wrap the definition of 'getUserEmail' in its own useCallback() Hook.
I am not sure on how this should be done.
Any ideas on what I could do?
Thanks
Wrap getUserEmail's value in a useCallback.
On every render, getUserEmail essentially becomes a 'new' function.
When there's a function in the deps array of a useEffect or other such hooks, React checks it by reference. Since each component function execution/rerender leads to the creation of a new function, your useEffect hook will actually run every single time, sending you into a re-render loop (because it'll run the useEffect, update the state with setProfile, which in turn will trigger another execution, where getUserEmail is different again, leading to the useEffect to run again and so on).
const getUserEmail = useCallback((): string => {
return claims.filter(x => x.name === "email")[0]?.value.toString();
}, [claims]);
This should give you a memoized callback that will only be recreated if claims changes. Since claims comes from your context, this should be safe as a dependency.
The reason why you are getting error about order of hooks is I think because of this:
profile ?
<article>
<h1>This profile belongs to {UserName()}</h1>
<h2>{profile.name}</h2>
</article>
: <div>Loading...</div>
If UserName is a component you should not call it as function, rather as element <UserName/>. When you call it as function react thinks some of the hooks which you call inside it belong to the parent component - this combined with condition profile ? could give you the error.

useCallback vs useEffect in React

What's the different between useEffect when you pass it dependencies as the second parameter and useCallback?
Don't both essentially run the function/code passed as the first parameter whenever the dependencies passed as the second parameter change?
From what I've read the two hooks are intended to serve different purposes, but my question is whether they in actuality could be used interchangeably because they functionally do the same thing
They're too different.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
You can't switch useEffect with useCallback alone because you also need the logic to run the newly created function. (I suppose you could implement this if you used a ref as well, but that'd be quite strange.)
You can't switch useCallback with useEffect because you very often don't want to run the newly created function immediately - rather, you usually want to pass it as a prop to some other component.
useCallback primarily exists for optimization purposes, to reduce re-renders of a child component.
No, They are not same.
useEffect - is used to run side effects in the component when something changes. useEffect does
not return you anything. It just runs a piece of code in the component.
useCallback - Whereas useCallback returns a function, it does not execute the code actually. It is important to understand that
functions are objects in Javascript. If you don't use useCallback, the function you define inside the component is
re-created whenever the component rebuilds.
Example
Consider this example, this component will go in a infinite loop. Think Why?
const TestComponent = props => {
const testFunction = () => {
// does something.
};
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
Because on each render the testFunction
would be re-created and we already know that ueEffect will run the code when ever the testFunction changes. And since testFunction changes on each render, the useEffect will keep on running, and hence an infinite loop.
To fix this, we have to tell react, hey please don't re-create the testFunction on each render, create it only on first render (or when something changes on which it depends).
const TestComponent = props => {
const testFunction = useCallback(() => {
// does something.
}, []);
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
This won't be a infinite loop, since instance of testFunction will change only on first render and hence useEffect will run only once.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
Let's take an example, If I run the below code and click the first button it'll always rerender MemoComponent as well. Why because every time
we are passing new onClick function to this. To avoid re-rendering of MemoComponent what we can do is wrap onClick to useCallback. Whenever you want to create a new function pass state to the dependence array.
If you want to perform some action on state change you can write inside useEffect.
const Button = ({ onClick }) => {
console.log("Render");
return <button onClick={onClick}>Click</button>;
};
const MemoComponent = React.memo(Button);
export default function Home() {
const [state, setState] = useState(1);
useEffect(() => {
console.log(state); // this will execute when state changes
}, [state]);
const onClick = () => {};
// const onClick = useCallback(() => {},[])
return (
<main>
<button onClick={() => setState(1 + state)}>{state}</button>
<MemoComponent onClick={onClick} />
</main>
);
}
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
OR
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, []);
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change. You should use it when a function is a dependency of a side effect e.g. useEffect.
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.

ReactJS functional component props issue inside useEffect in Hooks

I'm using a functional component as a child with props from parent. In the props i have a value array in which i'm getting as empty([]) inside useEffect and after some time, the same is getting rendered in the UI. I'm using useEffect to call a function and set the state only once as below and i want it to be like that. Here value is the props and checkEmpty is the function i'm checking and setting the state, completed. Is there any way to invoke the function inside useEffect once the value array is filled. But i want the useEffect to be invoked only once and needs to change the state completed with out letting it to an infinite loop.
useEffect(() => {
checkEmpty(value)?setCompleted(false):setCompleted(true)
}, [])
You can pass the value as a dependency of useEffect, and the hook only run when the value is changed.
useEffect(() => {
checkEmpty(value) ? setCompleted(false) : setCompleted(true);
}, [value])

Resources