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

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

Related

How to access data of Child Component from parent component in react js

I am doing a project where i have a toast function which implements toast there i call the function of fetching data from api and updating my state so that whenever i click the update feed button fetching data from api function called, updation of state and toast of success appears. Now the question is i have a component of categories post displays seperate category post inside of all post component which has the function to display toast, how could i pass the updated state,fetching data from api function from child component that is category post component to parent component that is all post component to implement toast for category component.
If I understand your question correctly -- at a high level, you're trying to figure out how to update a state variable of a parent component from within a child component. Easiest way would be with the useState hook, and then by passing the setState function to the child component.
const ParentComponent = () => {
const [state, setState] = useState([])
useEffect(() => {
// logic that will be executed every time the state variable changes
}, [state])
return <ChildComponent setState={setState} />
}
const ChildComponent = ({setState}) => {
const handleClick = () => {
setState((currentState) => currentState.concat(1))
}
return <Button onClick={handleClick} />
}
Edit: To answer your question from the comment -- a few things to point out here:
You can pass a value to useState which will be the starting value of the variable. In our example, it's an empty array
setState has access to the current state, so you can push a value to an array with this syntax: setState((previousState) => previousState.concat(val))
useEffect is a hook which is invoked whenever there's a change in the value of the dependency (or dependencies) passed in the second argument. So by including state in its dependency array, we can execute whatever logic we want every time the value of the state variable changes
I would also recommend looking into useMemo. It similarly allows you to have aspects of your component logic that are re-executed only when values of certain variables change. For example:
const ParentComponent = () => {
const [state, setState] = useState([])
useEffect(() => {
// logic that will be executed every time the state variable changes
}, [state])
const renderCards = useMemo(() => {
return state.map(val => <SomeOtherComponent val={val}/>)
}, [state])
return (
<div>
{renderCards}
<ChildComponent setState={setState} />
</div>
)
}
By wrapping the function inside renderCards in the useMemo hook, the evaluated result is "memoized". And so it won't be executed on every render, unless the variable in the dependency array changes.
Passing down setState to a child component in order to trigger a re-render in the parent component is straightforward when it's an immediate child. If the child component is nested deeper, or there are multiple components that need to react to a change in a variable (e.g. light/dark mode) -- that's when you want to look into a state management tool like Redux or Context.
There are two ways I can think of to achieve what you are trying to do here, i.e. get the child component's state in a parent component.
You can make use of refs. You should be familiar with React hooks to use this approach. The documentation is really good.
You can also make use of a global state using either Redux or Context.

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.

react.js hook (too many re-renders)

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")
}, []);

React: How to pass state to child component and call function in child to use this state

I am implementing a component in functional components where there are several other child components in it passing data to each other. I need to pass data from parent to child component and call some function there to use it.
In class componenets we use componentdidupdate but could not understand how to do in functional component.
One idea is to use useEffect hook but could not do with it.
Im going to take a stab here, because we dont have context or code to go with.
useEffect accepts a dependency array to which it will react when a value or object reference changes
const ChildComponent = (props) => {
const {
valuePassedFromParent
} = props;
const actionFunction = (value) => {
//perform some tasks with value passed from parent when it changes
}
//this is similar to componentDidUpdate, but it reacts to changes in valuePassedFromParent though props since its in the useEffect dependency array
useEffect(() => {
//do something with valuePassedFromParent
actionFunction(valuePassedFromParent);
},[valuePassedFromParent]);
return (
<div>
</div>
)
}
You cas use useEffect to reproduce the behavior of componentdidupdate like this :
const [myState,setMyState] = useState();
useEffect(() => {
...
},[myState]);
The function use effect will run every time myState will be updated, like componentdiduptate would have do.
In your case, the state is given by the parent component if I understand well, so just replace the myState in the array dependency by the state given through the prop of your child component.

useEffect local state behavior after it get updated

In this reactjs doc example about custom hooks
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
we suppose tnhat is called by this Foo component
function Foo(props) {
const [state, setState] = useState({...});
...
useFriendStatus(friendID)
...
}
When handleStatusChange is invoked and the useEffect local state isOnline change, what will happend ?
will the Foo component get rendered more or less immediately (i know setState() is asynch) or it will wait until its own state or props gets updated ?
in my knowledge a custom hook will be invoked only when the component which call it is rendered or am i wrong ?
When handleStatusChange is invoked and the useEffect local state isOnline change, what will happend ?
will the Foo component get rendered more or less immediately (i know setState() is asynch) or it will wait until its own state or props gets updated ?
Setting state is what causes react components to rerender. As soon as you call setIsOnline, the component's state has been updated, and it rerenders more or less immediately.
in my knowledge a custom hook will be invoked only when the component which call it is rendered or am i wrong ?
Custom hooks are a convenience: they let you reuse code, or simply make your code more readable. But they don't change what react knows. React just knows that you called useState, useEffect, and later setIsOnline. It doesn't know whether that code is written inline or extracted to a helper method.
If React sees a call to useState while rendering, it will set up a state variable and a setter function. If you call that setter function, it will rerender the component.

Resources