The React Hooks docs has a FAQ entry: “ ” where they link to There is however a shortcoming in that approach which affects correctness is some important situations. How can I do data fetching with Hooks? this well written and detailed article by Robin Wieruch. As far as I can tell, there isn’t a widely available description of a more robust solution. There is nuance in getting it completely right so it seems worthwhile to fill this gap. I attempt to do that here. TL;DR You need something like from Class Components. Use the custom hook provided below. getDerivedStateFromProps useDerivedState() Data fetching means we probably need to keep state in sync with props. Consider a component that fetching something from the server to display to the user or base further action on. You probably want to store the result of the data fetching as state in the component and the call to the server probably depends on some prop that is passed to the component. This means that when the prop changes, we have to update the state correctly. That is, we need to keep the state in sync with the props. For example, consider a social app where a user has a list of friends. You are building a component call that displays the user’s name along with a list of his/her friends . FriendList The component is passed in the current user as prop and has to fetch the list of friends from the server, which is stored as state. When the user changes, the list of friends needs to be fetched from the server again. Keeping state in sync with props though useEffect() is problematic. The approach taken by Robin Wieruch in the article linked above is to use to sync the state to the prop. Here is some code that implements using this approach. useEffect() FriendList User = {name: ; id: } State = {loading: } | {loading: ; friends: User[]} { [state, setState] = React.useState<State>({loading: }) React.useEffect( { setState({loading: }) serverCallToGetFriendList(user, setState({loading: , friends}) ) }, [user]) ( <> <h1>{user.name}, your friends are:< li> ))} < > ) } type String string type true false ( ) function FriendList user: User const true => () true => friends false return /h1> {state.loading ? ( 'Loading...' ) : ( <ul> {state.friends.map(friend => ( <li> {friend.name} </ /ul> )} </ When the user prop changes, the is triggered, which updates the state to and makes the call to the server. When the server call returns the state is updated to . Where is the list fetched from the server. useEffect() {loading:true} {loading:false, friends} friends This approach has a problem. Effects are only run the rendering of the whole component tree is completed, and the browser completes painting and the effects of child components are run. This means when the prop changes, the state is out of sync for all these operations. after after after The lesser issue here, which may be acceptable in some cases, is that the browser will actually briefly display out of sync data to the user. In our example, the browser may briefly display the friends as belonging to the before it updates to ‘loading…’. previous user’s new user But there is a bigger issue. Given that the rendering was completed with out of sync data, child components that were passed the prop and state will see out of sync data. They might have scheduled effects based on that bad data. These effects will run before the state is corrected and will likely be doing bad things. The child components may also have set callbacks that will keep the bad data in their closures, and if triggered before the effect is run, will probably do something bad as well. Even if the above issues don’t materialize in specific use cases, having out of sync data surface in this manner is intrinsically problematic and should be avoided. Out of sync data should not be visible to child components. It makes reasoning about the program much harder when you have bad data moving around. useLayoutEffect() won’t solve the problem. Since the problem with using to sync the state to a prop is that it runs too late, we might consider which runs earlier. guarantees that the effect is run the browser paints. This is better. Bad data is passed to child components, but will get fixed before the paint, so is not visible to the user useEffect() useLayoutEffect() useLayoutEffect() before But what about effects scheduled by children based on the bad data? This is a tricky part of the semantics of . It turns out that if you call inside it forces the effects to run before the state is updated and before the paint. (Note, this is the reverse of what happens normally. Usually effects run after the paint.) This is clarified by Brian Vaughn from the React team . useLayoutEffect() setState() useLayoutEffect() here As part of this second, synchronous render [triggered by a setState() call in a layout effect], React also flushes the remaining/pending effects (including the passive ones- which would otherwise have not been flushed until a later frame). So is not a satisfactory solution. useLayoutEffect() Changing the key works, but it is a blunt tool. A perhaps non-obvious solution to the issue of keeping state in sync with props is to simply get React to destroy the underlying instance (and therefore state) of the component and create a new one when the prop changes. We can do this by whenever the prop changes. In our example, we could set the key of the component to . This effectively makes the prop a constant over the lifetime of the component instance, so we never have to worry about it changing. changing the key of the React component FriendList user.id This works. Child components are never rendered with bad data. But it is a blunt tool. It may not be desirable to destroy the instance for a number of reasons. You will lose all the other state from the instance, for example. Call setState() directly during render. Let us step back and consider what we are trying to do. We are trying to keep state in sync with props. There is an idiomatic to do this with Class Components and it is . We have to implement something similar in Hooks. There is a . The suggestion is to call directly during render when a prop change is detected to keep them in sync. Calling during render has the following semantics: getDerivedStateFromProps FAQ entry addressing exactly this setState() setState() [Y]ou can update the state right during rendering [a functional component]. React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive. The key to this is that render will re-run In particular, before any child component renders, so child components never see out of sync state. immediately. Applying this to we have the following: FriendList User = {name: ; id: } State = {loading: } | {loading: ; friends: User[]} { [state, setState] = React.useState<State>({loading: }) [localUser, setLocalUser] = React.useState(user) (user !== localUser) { setLocalUser(user) setState({loading: }) } React.useEffect( { (!state.loading) serverCallToGetFriendList(user, setState({loading: , friends}) ) }, [user, state]) ( <> <h1>{user.name}, your friends are:< li> ))} < > ) } type String string type true false ( ) function FriendList user: User const true // Save a local copy of the prop so we know when it changes. const // When the prop changes, update the local copy and the state that needs. // to be in sync with the prop. if true => () if return => friends false return /h1> {state.loading ? ( 'Loading...' ) : ( <ul> {state.friends.map(friend => ( <li> {friend.name} </ /ul> )} </ We keep a local copy of the prop to check for changes. When a change is detected we update the local copy and the state to keep it in sync with the prop. Now when the prop changes the state is updated as soon as the render completes, and the render re-runs with the updated state. Child components never see bad data. Note that we still have the , but it is only used to make the server call, and not to update the state on prop change. useEffect() Note however that since is asynchronous, the state is fixed only after the function returns. This means that you still have to deal with out of sync state for the rest of the function. What if you schedule effects locally based on the bad data? Do we have to be careful about that? Turns out we do not. setState() There is no official documentation, but testing reveals that effects scheduled during the render are discarded if is called during that render. It also does not get counted as being run for the dependency list change calculations. It short, it is a no-op. setState() We can actually do even better though and not have to worry about out of sync state even inside the render function. There some bookkeeping to take care of and this is a good time to extract the details to a custom hook. useDerivedState() Here is code for a custom hook (alluding to the Class Component equivalent): useDerivedState { [localState, setLocalState] = React.useState< | {init: } | { init: publicState: State depList: [] } >({init: }) currPublicState: State ( !localState.init || depList.length !== localState.depList.length || !localState.depList.every( depList[i] === x) ) { currPublicState = onDepChange() setLocalState({ init: , publicState: currPublicState, depList }) } { currPublicState = localState.publicState } publicSetState = React.useCallback( { setLocalState( { (!localState.init) () publicState = newState === ? (newState )(localState.publicState) : newState {...localState, publicState} }) }, [] ) [currPublicState, publicSetState] } export < >( ): [ , ( ) => ] function useDerivedState State onDepChange: () => State, depList: [] any State newState: State | ((state: State) => State) void const false true any false let if ( ) => x, i true else const ( ) => newState: State | ( ) ( ) => State state: State => localState if throw new Error const typeof 'function' as any return return is intended to be used similarly to . It returns a tuple just like . The difference is that instead of taking an initial state like , takes in a function to generate the state, and a dependency list. Whenever anything in the dependency list changes recalculates the state from the function and returns the new state useDerivedState() useState() [state, setState] setState() setState() useDerivedState() useDerivedState() synchronously. now becomes: FriendList { [state, setState] = useDerivedState<State> ( ) function FriendList user: User const ( ) . ( ) ( ) } ( ) => { {loading: } }, [user] return true React useEffect ( ) => { ( ) serverCallToGetFriendList( ) }, [state, user] if !state.loading return user, friends => setState( ) {loading: , friends} false return <> <h1>{user.name}, your friends are:</h1> {state.loading ? ( ) : ( )} </> 'Loading...' <ul> {state.friends.map( )} </ul> friend => ( ) <li> {friend.name} </li> Whenever the prop changes, the state is reset to synchronously which keeps the relationship between them easy to reason about. user {loading:true} Conclusion That hopefully gives you a new idiomatic way for data fetching in Hooks, and more generally, keeping state in sync with props. It is unfortunate that the official docs don’t provide more support for this and in fact suggests using which can lead to serious errors. Strangely the docs actively discourage the right approach by going of calling directly while rendering: “You probably don’t need it, in rare cases that you do”. As we can see, it is needed for the correct approach to data fetching, which is pretty widespread use case if there ever was one. useEffect() so far as to saying setState() About Me I'm building -- the future of eBooks. Qurika : Image Credit ccPixs.com