I have a state, that I only want to change when a certain funcitonal component is initialised. So I intend to do somethin like this:
export default function SalesFeedPage(){
const {salesFeed} = useSelector((state) => state.feedReducer);
const dispatch = useDispatch();
// i want to do sth like this
// useEffect(() => dispatch(loadSalesFeed()), []);
// or
// dispatch(loadSalesFeed());
return (
<div>
hello
{salesFeed}
</div>
)
}
This doesn't work since it infinitely re-renders the SalesFeedPage.
Is there a way to achieve what I want in a funcitonal component?
Just keeping the first useEffect should work, but make sure to only return cleanup functions inside of your useEffect, not your dispatch return value. Like so,
useEffect(() => { dispatch(loadSalesFeed()) }, [])
You have to use,
useEffect(() => dispatch(loadSalesFeed()), [])
if you use Dispatch outside the useEffect without dependency array, your component continuously getting re rendering. That because ,when you component mounting your dispatch function get called, so your redux state get changed, then again your component get rendered. Then your dispatched is called again. Then this make your component get render again. This lead to infinite rendering cycle.
Hope this help to you!
Related
Let's say I have this simple dummy component:
const Component = () => {
const [state, setState] = useState(1);
setState(1);
return <div>Component</div>
}
In this code, I update the state to the same value as before directly in the component body. But, this causes too many re-renders even if the value stayed the same.
And as I know, in React.useState, if a state value was updated to the same value as before - React won't re-render the component. So why is it happening here?
However, if I try to do something simillar with useEffect and not directly in the component body:
const Component = () => {
const [state, setState] = useState(1);
useEffect(()=>{
setState(1);
},[state])
return <div>Component</div>
}
This is not causing any infinte loop and goes exactly according to the rule that React won't re-render the component if the state stayed the same.
So my question is: Why is it causing an infinte loop when I do it directly in the component body and in the useEffect it doesn't?
If someone has some "behind the sences" explanation for this, I would be very grateful!
TL;DR
The first example is an unintentional side-effect and will trigger rerenders unconditionally while the second is an intentional side-effect and allows the React component lifecycle to function as expected.
Answer
I think you are conflating the "Render phase" of the component lifecycle when React invokes the component's render method to compute the diff for the next render cycle with what we commonly refer to as the "render cycle" during the "Commit phase" when React has updated the DOM.
See the component lifecycle diagram:
Note that in React function components that the entire function body is the "render" method, the function's return value is what we want flushed, or committed, to the DOM. As we all should know by now, the "render" method of a React component is to be considered a pure function without side-effects. In other words, the rendered result is a pure function of state and props.
In the first example the enqueued state update is an unintentional side-effect that is invoked outside the normal component lifecycle (i.e. mount, update, unmount).
const Component = () => {
const [state, setState] = useState(1);
setState(1); // <-- unintentional side-effect
return <div>Component</div>;
};
It's triggering a rerender during the "Render phase". The React component never got a chance to complete a render cycle so there's nothing to "diff" against or bail out of, thus the render loop occurs.
The other example the enqueued state update is an intentional side-effect. The useEffect hook runs at the end of the render cycle after the next UI change is flushed, or committed, to the DOM.
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1); // <-- intentional side-effect
}, [state]);
return <div>Component</div>;
}
The useEffect hook is roughly the function component equivalent to the class component's componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods. It is guaranteed to run at least once when the component mounts regardless of dependencies. The effect will run once and enqueue a state update. React will "see" that the enqueued value is the same as the current state value and won't trigger a rerender.
Similarly you could use the useEffect hook and completely remove the dependency array so it's an effect that would/could fire each and every render cycle.
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1);
});
return <div>Component</div>;
}
Again, the useEffect hook callback is guaranteed to be invoked at least once, enqueueing a state update. React will "see" the enqueued value is the same as the current state value and won't trigger a rerender.
The takeaway here is to not code unintentional and unexpected side-effects into your React components as this results in and/or leads to buggy code.
When invoking setState(1) you also trigger a re-render since that is inherently how hooks work. Here's a great explanation of the underlying mechanics:
How does React.useState triggers re-render?
first off - Happy Friday!
I just came on here to see if anyone had any input to an issue that I am seeing in my ReactJs application. So I have a functional component renderViews and in that functional component, there are multiple views to render. Then within the renderViews I have another functional component carDetailsView and I try to make a call to an api when that particular component appears(as a modal). requestCarsDetails() should only be called when that component appears so thats why I nested a useEffect hook in the carDetailsView. But that causes an issue:
Rendered more hooks than during the previous render
.Please see code below:
const renderViews = () = > {
useEffect(()=> {
requestCarInfos()
.then((res) => {
setCars(cars);
});
}, []);
const carDetailsView = () => {
useEffect(() => {
requestCarDetails()
.then((res) => {
setDetails(res.details);
});
}, []);
return (<div>carDetailsView</div>)
}
return (<div>{determineView()}</div>)
}
The useEffect that is being used at the top level works fine. The issue only appeared after I added the second useEffect which is in the carDetailsView. Any help or advice is appreciated. Thanks!
Its a rule of hooks.
https://reactjs.org/docs/hooks-rules.html
Only Call Hooks at the Top Level
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.
React relies on the order in which Hooks are called.
As long as the order of the Hook calls is the same between renders, React can associate some local state with each of them.
if we put a Hook call inside a condition we can skip the Hook during rendering, the order of the Hook calls becomes different:
React wouldn’t know what to return for the second useState Hook call. React expected that the second Hook call in this component corresponds to the persistForm effect, just like during the previous render, but it doesn’t anymore. From that point, every next Hook call after the one we skipped would also shift by one, leading to bugs.
This is why Hooks must be called on the top level of our components. If we want to run an effect conditionally, we can put that condition inside our Hook:
use the lint https://www.npmjs.com/package/eslint-plugin-react-hooks
this is a caveat of using functional components, on each render everything inside the functional component gets kind of executed. so react needs to maintain the list of all hooks which have been defined when the component was created. think of it as an array.
on each render, useState will return the value for you. if you understand this, you will understand what stale state also means. ( stale state can happen, when closures occur within these components )
Something like that?
const CarDetailsView = () => {
React.useEffect(() => {
console.log("Running CarDetailsView useEffect...") ;
},[]);
return(
<div>I amCarDetailsView</div>
);
};
const Views = () => {
const [showCarDetails,setShowCarDetails] = React.useState(false);
const toggleCarDetails = () => setShowCarDetails(!showCarDetails);
React.useEffect(() => {
console.log("Running Views useEffect...") ;
},[]);
return(
<div>
<div>I am Views</div>
<button onClick={toggleCarDetails}>Toggle car details</button>
{showCarDetails && <CarDetailsView/>}
</div>
);
};
const App = () => {
return(<Views/>);
};
ReactDOM.render(<App/>,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root"/>
I am new to React, so pointers also welcome.
I am populating an array with the json of an api call:
fetch('/rpa').then(rpa => rpa.json()).then(data => data.rpa).then(nestedData=>nestedData.forEach(item => jsonRespnse.push(item)));
console.log(jsonRespnse)
Logging to the console shows the data as I would expect. However, putting that data in as part of my return, I am not getting anything:
return (
<div>
{rpaName.map((rpaItem, i) => (
<div>
<div className='headerContainer' onClick={()=>toggle(i)}>
<h4 className='rpaHeader'>{rpaItem}</h4><span className='rpaSpan'>{selected === i ? '-': '+'}</span>
</div>
<div className={selected === i ? 'rpaButton show': 'rpaButton'}>
<button onClick={()=>sendData(rpaItem)}>Start{rpaItem}</button><button>Stop{rpaItem}</button>
</div>
<br></br>
</div>))}
</div>);}
I am assuming its a timing thing, with the rendering taking place before the array can be populated, when I hard code an array it works fine.
If anyone could point me in the right direction that would be appreciated.
The only way to make the component rerender within itself is to use state.
In React world, since you didn't provide the full component, I'm assuming you're using functional components, in which you have hooks such as useState, and useEffect.
useState is where you'd place your changing variables to.
Example.
function MyComponent() {
// the first variable here is the actual value of the state, the next is the function to change the state.
const [myState, setMyState] = React.useState();
// when we move over to useEffect, is the hook that'd typically use to perform fetch requests for example.
React.useEffect(() => {
fetch(...).then(response => response.json()).then(setMyState);
}, [])
return <div>{myState}</div>
}
When the state gets a new value, it will rerender the component to reflect the new change.
Since you are using functional components, you can use the useEffect hook of react to perform the API call before rendering your component.
Then you can use the useState hook to declare a state variable to hold the fetched data.
Sample code:
import React, { useState, useEffect } from 'react';
const yourComponent = () => {
const [ data, setData ] = useState([]);
useEffect(() => {
fetch('<URL>').then(response => response.json()).then(responseArr => setData(responseArr)));
}, []);
return(
//Rest of the code (Now you can use the fetched data as an array since "data" state's been populated with the data fetched from the API call)
);
}
you're probably wanting to set the request array as a state object so something like
import React, { useState } from 'react';
function someReactComponent() {
// Declare a new state variable, which we'll call "count"
const [fetchResponse, setFetchResponse] = useState([]);
fetch('/yourfetchurl').then(response => response.json()).then(responseArr => setFetchResponse(responseArr)));
return (
<div>
{fetchResponse.map((res, i) => {
return (
<div key={i}>
{res.whatever}
</div>
);
})}
</div>
);
}
Thanks all for your responses - I managed to get there with a combination of all of them really, so thankyou.
Once I got the useState + useEffect combo in there, it was just a case of how the JSON was being put into the array, this is what was giving me the error, I had to access they key first:
getList().then(items => setRpaList(items.rpa));
Thankyou for your help.
You're going to want to have a useEffect to make the network call in your component, so that it fetches the data after render, and a useState to bind the variable data to. Then once the async call resolves (i.e. in the callback), call the state setter function with the resulting data to refresh the data on the page.
This article provides a good explanation: https://www.digitalocean.com/community/tutorials/how-to-call-web-apis-with-the-useeffect-hook-in-react
You can also start a spinner on render, and kill the spinner once the fetching callback runs.
Note that console.log does not guarantee that it will log the data as it was at the time the statement was executed, which means that the jsonResponse when that line was run may be (in this case, it is) different then what you observed outputted in your console.
How do I cause re-rendering to the component
function Bookmarks({ data }: any) {
const bookmarkedBlogs = useBookmarks().getBookmarkedBlogs(data.allMdx.nodes);
.....
}
when bookmarks change in the hook
function useBookmarks() {
const [bookmarks, setBookmarks, accessDenied] = useLocalStorage<BlogType['id'][]>('bookmarks', []);
const getBookmarkedBlogs = (blogs: BlogType[]) => {
return blogs.filter(checkIsBookmarked)
};
because as of now, even if I toggle bookmarks, the getBookmarkedBlogs function doesn't execute except in the initial render of the component.
How the implementation of useLocalStorage, and how you toggle bookmarks?
localStorage changes don't notify your every hooks except your make a observer model
if you don't make observer model, toggle bookmark in other hooks or other ways wouldn't notify your this hook, so it don't rerun
Your hook doesn't actually work.
It seems like you want a hook that subscribes to updates from some source outside the React render tree.
Here's a simple example of something that works like that:
function MyComp() {
let eventValue = useEventListener()
return (
<div>
{eventValue}
</div>
)
}
function useEventListener() {
let [ value, setValue ] = React.useState(1)
React.useEffect(() => {
setTimeout(() => setValue(5), 1000)
}, [])
return value
}
What makes it work is that the custom hook invokes a built-in hook when data should change. The React Hooks framework handles the rest.
Instead of a setTimeout, you could subscribe to an event stream, or listen for IPC messages, or something else. But:
The custom hook has to actually return something
The custom hook can only trigger a re-render by invoking a builtin hook
The custom hook must set up its subscribe-to-changes logic on its first invocation, which is often best handled by useEffect configured to run once
I know the conventional way when using hooks is to fetch the data using the useEffect hook. But why can't I just call axios in the functional component instead of a hook and then set the data.
Basically, I am asking what is wrong with doing this:
const [users, setUsers] = useState(null);
axios.get("some api call")
.then(res => setUsers(res.data))
Here, I do not use useEffect, what could go wrong?
Making a request and changing the state on render like that will cause the component to re-render forever.
Every time the component renders, e.g. due to props changing or due to hooks changing, that axios.get request gets called. When it gets a response, it will update the state. Then, because the state has changed, the component re-renders, and axios.get is called again. And the state changes again, and the request is made again, forever.
Prefer useEffect(() => code... , []).
That said, you can also do it while avoiding an infinite loop but it's a very bad practice and I don't recommend it.
Yes, you will have a re-render but you won't have an infinite loop. Use useState's lazy init function.
const [users, getUsers] = useState(() => {
axios.get("some api call")
.then(res => getUsers(res.data))
});
Best practice is :
const [users,getUsers]= useState();
useEffect ( () => {
axios.get("some api call")
.then(res=>getUsers(res.data))
}, []);