React lifecycle methods - reactjs

I have been doing react for a while and am familiar with some lifecycle methods but not so familiar with others
e.g. didMount is clearly for ajax requests or calling data from an api then loading it into the app
I think I have conquered shouldComponentUpdate, and have realised it is purely there for performance
but static getDerivedStateFromProps I cant really get my head around. is that for performance or does that add something else to the app?
and also componentDidUpdate, is this for performance again or where is a good example where I can use these?
clearly some methods are necessary to perform actions and actual requests. clearly some are there to improve performance etc. just would like to get some context around didUpdate and getDerived
thanks

Yo can understand the lifecycle hooks in react through this blog : https://medium.com/#baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d

Lifecycle methods made to allow you to run code at particular times in the process.
componentDidMount() is invoked immediately after a component is inserted into the tree, we commonly use it to make API requests.
Using React Hooks
useEffect(() => {
makeApiRequest()
}, [])
componentDidUpdate() is invoked immediately after updating occurs (in the state or props). This method is not called for the initial render. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request
Using React Hooks
useEffect(() => {
doYourStuff()
})
The difference between componentDidUpdate and the Hook above that the Hook will be called in the initial render also. There is no 100% alternative to the componentDidUpdate() method.
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions.
Using React Hooks
useEffect(() => {
return () => {
cleanUp()
}
})
getDerivedStateFromProps() is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read More -> React Lifecycle React Hooks

Related

useEffect or useMemo for API functions?

which is the best hook for dispatching API calls in a component. Usually I use useMemo for calling the API on the first render, and useEffect if I need extra side effects, is this correct? Becouse sometimes I get the following error:
'''index.js:1 Warning: Cannot update a component (Inscriptions) while rendering a different component (PaySummary). To locate the bad setState() call inside PaySummary, follow the stack trace as described in ...''''
That happens when I route to a component and rapidly change to another one, it doesn't "affect" the general behaivour becouse if i go back to the previous component it renders as expected correctly. So how should I do it?
Calling an API is a side effect and you should be using useEffect, not useMemo
Per the React docs for useEffect:
Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before.
Per the React docs for useMemo:
Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.
Performing those side effects (and modifying state) during rendering or with useMemo is the reason you encounter the errors you mention.
basically I rather to use useEffect in componentDidMount manner, with no dependency like below
useEffect(() => {
// Api call , or redux async action here...
}, [])
for calling api's at component mount state.
most of the time i find my self using useMemo for memoising the data at functional Component render level, for preventing the variable re-creation and persist the created data between renders except the dependency changes.
but for the context of your question, there is a hook called useLayoutEffect which is primarily used for actions to happen before painting the DOM, but as i said basically most of the time in projects i find calling apis in a simple useEffect with no dependencies aka, the did mount of your component, in order to load the required data for component!
A bit late but, while everything mentioned above is completely true; the error
'''index.js:1 Warning: Cannot update a component (Inscriptions) while rendering a different component (PaySummary). To locate the bad setState() call inside PaySummary, follow the stack trace as described in ...''''
Has to do with the fact that the API call is Asynchronous and when you rapidly change the pages, the set state call (for updating the data returned from the API call I assume) is still waiting to be called after the data is returned from the API. So, you have to always clean up your Async functions in useEffect to avoid this error.

React useEffect hook not calling mocked function

I am new to React hooks. I'm using a useEffect() hook in a component, and that hook calls a function, searchForVideos() from my props:
useEffect(() => {
props.searchForVideos();
}, [currentPage]);
That function is mocked in my unit tests using Jest:
const searchForVideos = jest.fn();
So, based on my understanding, useEffect() should run for the first time immediately after component render. I can put a console.log() statement in the useEffect() callback and it does print to the console. However, this expect() statement fails:
const component = mountComponent();
setImmediate(() => {
expect(searchForVideos).toHaveBeenCalled();
});
This is strange, because I've confirmed the hook is running, and if it is running it should call the function. However, that line always fails.
Is there something I should know about to make the mocked functions work well with React hooks?
Update
In response to a comment, I made a change that fixed the problem. I do not understand why this worked, though.
const component = mountComponent();
requestAnimationFrame(() => {
expect(searchForVideos).toHaveBeenCalled();
done();
});
So I just replaced setImmediate() with requestAnimationFrame(), and now everything works. I've never touched requestAnimationFrame() before. My understanding of setImmediate() would be that it basically queues up the callback at the back of the event queue right away, so any other JavaScript tasks in the queue will run before it.
So ideally I'm seeking an explanation about these functions and why this change worked.
As far as your secondary question about why requestAnimationFrame fixed it, see the documentation excerpts below. It just gets into the specific timing of useEffect and useLayoutEffect -- specifically "useEffect is deferred until after the browser has painted". requestAnimationFrame delays your test code in a similar fashion. I would expect that if you changed your code to use useLayoutEffect, the original version of your test would work (but useEffect is the appropriate hook to use for your case).
From https://reactjs.org/docs/hooks-reference.html#timing-of-effects:
Unlike componentDidMount and componentDidUpdate, the function passed
to useEffect fires after layout and paint, during a deferred event.
This makes it suitable for the many common side effects, like setting
up subscriptions and event handlers, because most types of work
shouldn’t block the browser from updating the screen.
However, not all effects can be deferred. For example, a DOM mutation
that is visible to the user must fire synchronously before the next
paint so that the user does not perceive a visual inconsistency. (The
distinction is conceptually similar to passive versus active event
listeners.) For these types of effects, React provides one additional
Hook called useLayoutEffect. It has the same signature as useEffect,
and only differs in when it is fired.
Although useEffect is deferred until after the browser has painted,
it’s guaranteed to fire before any new renders. React will always
flush a previous render’s effects before starting a new update.
From https://reactjs.org/docs/hooks-reference.html#uselayouteffect:
The signature is identical to useEffect, but it fires synchronously
after all DOM mutations. Use this to read layout from the DOM and
synchronously re-render. Updates scheduled inside useLayoutEffect will
be flushed synchronously, before the browser has a chance to paint.
Prefer the standard useEffect when possible to avoid blocking visual
updates.

Render is called twice when fetching data from a REST API

I am trying to interact with a REST API using React, and I have realized that when I fetch the data, render is called once without the data, and then again with the data.
This throws an exception when I try to process this data, but I can use an if statement to check if data is null or not. However, I am not sure if that's needed.
class App extends Component {
state = {
TodoList: {},
};
componentWillMount() {
axios.get("http://localhost:5001/1").then((response) => {
this.setState({
TodoList: response.data,
});
});
}
render() {
console.log(this.state);
return <h1>hello </h1>;
}
}
This is what I see in in the console:
That's perfectly normal.
Your App component flow as below:
Execute render method to load the component
execute codes in componentDidMount
Calling axios.get which is async operation
Receive data from step 2, update component state by using this.setState
App component detected there's an update on state, hence execute render method to load component again
Hence, you should definitely handle the case where this.state.TodoList has no data, which happened at first load
UPDATES:
component lifecycle componentWillMount is now deprecated which means you shouldn't be using it anymore. Replace it with componentDidMount instead. Functionally wise they should be no difference in your example
Initially, render method is called after cwm method. So console log shows the state's empty value first time.
But you have run an async operation in cwm method, so after it is done, the setstate method is called which causes the render method to run again.
Note: ComponentWillMount, componentWillUpdate and componentWillUpdate props method are deprecated.
You should move this API call to componentDidmount or ComponentDidUpdate method.
However, event after this, your console log will appear twice- one for initial render and second for setstate called after API call.
Note : componentWillMount is deprecated and will work until version 17.
Source - https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
Most suitable React Lifecycle for calling APIs:
componentDidMount is the most prefered method to perform asynchronous tasks like API calls, setTimeouts , etc..
It'll work as -
On componentDidMount your API gets called
As per lifecycle order, lastly render method will be called. (Still API hasn't returned response). Your UI is displayed at initial paint.
Once API gets response, you use this.setState() which will force a re-render operation.
Again your UI changes are updated
Remember : this.setState() will only be called once whether you have called it once or more than once in lifecycle method

How to migrate to new React lifecycle API?

As you know some lifecycle methods like componentWillReceiveProps are now deprecated.
Well, my code used this lifecycle method and now I have no idea how to migrate to the new API of React.
Here is how I used:
componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
In the react blog I could read that...
Together with componentDidUpdate, this new lifecycle should cover all
use cases for the legacy componentWillReceiveProps.
... but how to implement this?
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
Note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
you can use getDerivedStateFromProps or componentDidMount, there is sequence to running lifecycles :
getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate

Redux unsubscribe within componentWillUnmount still calls subscribe callback

I am manually hooking a React component up to a Redux store. Yes, I realize Dan Abramov of Redux recommends against this. I will be getting around to looking at react-redux eventually, but for now I want to understand what's going on.
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
const storeState = store.getState();
this.setState({
someData: storeState.someData
});
}.bind(this));
}
componentWillUnmount () {
this.unsubscribe();
}
This is some code I have in a tab page component. These tab pages get swapped in and out in a parent component as the user changes tabs to display different data. I have stepped through the debugger and confirmed that as a user changes tabs, the following occurs in order:
The previous tab is unmounted (componentWillUnmount fires and this.unsubscribe() is called. this is the previous tab component, as expected)
The new tab is mounted. (componentDidMount fires and this is the new tab component, as expected)
setState gets called in the subscribe callback with this being the PREVIOUS TAB, and react complains with an error:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Tab component.
This seems strange to me. Shouldn't calling unsubscribe as I have prevent this callback from being called on the unmounted component? The post that I'm following along with seems to suggest that doing what I've done should make this warning go away, and is simply questioning if this is a good idea. But in my case, it persists.
I don't think it matters, but I am using the ES6 "class" syntax whereas the original appears not to be.
subscribe(listener: () => void): Unsubscribe;
Lets read subscribe method doc.
The subscriptions are snapshotted just before every dispatch() call.
If you subscribe or unsubscribe while the listeners are being invoked,
this will not have any effect on the dispatch() that is currently in
progress. However, the next dispatch() call, whether nested or not,
will use a more recent snapshot of the subscription list.
Since you first have called dispatch method, all listeners are invoken and your unsubsribe call will only take place on next dispatch call.

Resources