220

Considering below hooks example

   import { useState } from 'react';

   function Example() {
       const [count, setCount] = useState(0);

       return (
           <div>
               <p>You clicked {count} times</p>
               <button onClick={() => setCount(count + 1)}>
                  Click me
               </button>
          </div>
        );
     }

Basically we use this.forceUpdate() method to force the component to re-render immediately in React class components like below example

    class Test extends Component{
        constructor(props){
             super(props);
             this.state = {
                 count:0,
                 count2: 100
             }
             this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component 
        }
        setCount(){
              let count = this.state.count;
                   count = count+1;
              let count2 = this.state.count2;
                   count2 = count2+1;
              this.setState({count});
              this.forceUpdate();
              //before below setState the component will re-render immediately when this.forceUpdate() is called
              this.setState({count2: count
        }

        render(){
              return (<div>
                   <span>Count: {this.state.count}></span>. 
                   <button onClick={this.setCount}></button>
                 </div>
        }
 }

But my query is How can I force above functional component to re-render immediately with hooks?

Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
  • 1
    Can you post a version of your original component that uses the `this.forceUpdate()`? Maybe there's a way to accomplish the same thing without that. – Jacob Nov 08 '18 at 20:04
  • The last line in setCount is truncated. It's unclear what's the purpose of setCount in its current state. – Estus Flask Nov 08 '18 at 20:42
  • That’s just an action after this.forceUpdate(); I added that just to explain about this.forceUpdate() in my question – Hemadri Dasari Nov 09 '18 at 02:26
  • 1
    For what it's worth: I was wrestling with this because I thought I needed a manual re-render, and finally realized that I simply needed to move an externally held variable into a state hook and leverage the setting function, which fixed all my problems without a re-render. Not to say that it's _never_ needed, but it's worth taking a third and fourth look to see if it's _actually_ needed in your specific use case. – Alexander Nied Apr 04 '20 at 16:51

20 Answers20

145

This is possible with useState or useReducer, since useState uses useReducer internally:

const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);

forceUpdate isn't intended to be used under normal circumstances, only in testing or other outstanding cases. This situation may be addressed in a more conventional way.

setCount is an example of improperly used forceUpdate, setState is asynchronous for performance reasons and shouldn't be forced to be synchronous just because state updates weren't performed correctly. If a state relies on previously set state, this should be done with updater function,

If you need to set the state based on the previous state, read about the updater argument below.

<...>

Both state and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with state.

setCount may not be an illustrative example because its purpose is unclear but this is the case for updater function:

setCount(){
  this.setState(({count}) => ({ count: count + 1 }));
  this.setState(({count2}) => ({ count2: count + 1 }));
  this.setState(({count}) => ({ count2: count + 1 }));
}

This is translated 1:1 to hooks, with the exception that functions that are used as callbacks should better be memoized:

   const [state, setState] = useState({ count: 0, count2: 100 });

   const setCount = useCallback(() => {
     setState(({count}) => ({ count: count + 1 }));
     setState(({count2}) => ({ count2: count + 1 }));
     setState(({count}) => ({ count2: count + 1 }));
   }, []);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • How does `const forceUpdate = useCallback(() => updateState({}), []);` work? Does it even force an update? – Dávid Molnár Nov 07 '19 at 10:44
  • 4
    @DávidMolnár `useCallback` memoizes `forceUpdate`, so it stays constant during component lifespan and can be passed as a prop safely. `updateState({})` updates the state with new object on each `forceUpdate` call, this results in a re-render. So yes, it forces an update when being called. – Estus Flask Nov 07 '19 at 11:02
  • 4
    So, the `useCallback` part is not really necessary. It should just work fine without it. – Dávid Molnár Nov 07 '19 at 11:03
  • And it would not work with `updateState(0)` because `0` is a primitive data type? Must it be an object or would `updateState([])` (i.e. usage with an array) also work? – Andru Oct 22 '20 at 09:47
  • 1
    @Andru Yes, a state is updated once because 0===0. Yes, an array will work because it's an object too. Anything that doesn't pass equality check can be used, like `updateState(Math.random())`, or a counter. – Estus Flask Oct 22 '20 at 10:17
  • @EstusFlask Actually the comparison is using `Object.is` - which is different as `Number.NaN` equals itself for identity comparison (and similarly +0 and -0 are different). – paul23 Feb 06 '21 at 01:03
  • 1
    One negligible difference between `setState` and `forceUpdate` is that, `forceUpdate` skips `shouldComponentUpdate` invocation. But with hooks there is no option to skip `React.memo`. – Easwar Feb 18 '21 at 13:26
72

React Hooks FAQ official solution for forceUpdate:

const [_, forceUpdate] = useReducer((x) => x + 1, 0);
// usage
<button onClick={forceUpdate}>Force update</button>

Working example

const App = () => {
  const [_, forceUpdate] = useReducer((x) => x + 1, 0);

  return (
    <div>
      <button onClick={forceUpdate}>Force update</button>
      <p>Forced update {_} times</p>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script>
<script>var useReducer = React.useReducer</script>
<div id="root"></div>
ford04
  • 66,267
  • 20
  • 199
  • 171
70

Generally, you can use any state handling approach you want to trigger an update.

With TypeScript

codesandbox example

useState

const forceUpdate: () => void = React.useState({})[1].bind(null, {})  // see NOTE below

useReducer (recommended)

const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void

as custom hook

Just wrap whatever approach you prefer like this

function useForceUpdate(): () => void {
  return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}

How this works?

"To trigger an update" means to tell React engine that some value has changed and that it should rerender your component.

[, setState] from useState() requires a parameter. We get rid of it by binding a fresh object {}.
() => ({}) in useReducer is a dummy reducer that returns a fresh object each time an action is dispatched.
{} (fresh object) is required so that it triggers an update by changing a reference in the state.

PS: useState just wraps useReducer internally, so use reducer to reduce complexity. source

NOTE: Referential instability

Using .bind with useState causes a change in function reference between renders.
It is possible to wrap it inside useCallback as already explained in this answer here, but then it wouldn't be a sexy one-liner™. The Reducer version already keeps reference equality (stability) between renders. This is important if you want to pass the forceUpdate function in props to another component.

plain JS

const forceUpdate = React.useState({})[1].bind(null, {})  // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]
Qwerty
  • 29,062
  • 22
  • 108
  • 136
  • Wouldn't this result in hooks being called a varying number of times between renders if `forceUpdate` is called conditionally, which would break the rules of hooks and potentially leave hooks accessing the wrong data? – user56reinstatemonica8 Mar 02 '21 at 18:49
  • 1
    @user56reinstatemonica8 This is basically just a state assignment which triggers the render, nothing quirky. – Qwerty Mar 02 '21 at 19:27
  • 4
    I had to use the following for the typescript useState solution `const forceUpdate: () => void = React.useState({})[1].bind(null, {});` otherwise I got a type error that the useState type was wrong – SamB Dec 20 '21 at 00:10
43

As the others have mentioned, useState works - here is how mobx-react-lite implements updates - you could do something similar.

Define a new hook, useForceUpdate -

import { useState, useCallback } from 'react'

export function useForceUpdate() {
  const [, setTick] = useState(0);
  const update = useCallback(() => {
    setTick(tick => tick + 1);
  }, [])
  return update;
}

and use it in a component -

const forceUpdate = useForceUpdate();
if (...) {
  forceUpdate(); // force re-render
}

See https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts and https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver.ts

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
  • 3
    From what I understand from hooks, this might not work as `useForceUpdate` will return a new function everytime the function re-renders. For `forceUpdate` to work when used in a `useEffect`, it should return `useCallback(update)` See https://kentcdodds.com/blog/usememo-and-usecallback – Martin Ratinaud Sep 12 '19 at 04:54
  • Thanks, @MartinRatinaud - yes, it might cause a memory leak without useCallback (?) - fixed. – Brian Burns Sep 13 '19 at 07:20
32

Alternative to @MinhKha's answer:

It can be much cleaner with useReducer:

const [, forceUpdate] = useReducer(x => x + 1, 0);

Usage: forceUpdate() - cleaner without params

nullspace
  • 870
  • 10
  • 12
16

You can simply define the useState like that:

const [, forceUpdate] = React.useState(0);

And usage: forceUpdate(n => !n)

Hope this help !

Minh Kha
  • 1,052
  • 8
  • 13
14

You should preferably only have your component depend on state and props and it will work as expected, but if you really need a function to force the component to re-render, you could use the useState hook and call the function when needed.

Example

const { useState, useEffect } = React;

function Foo() {
  const [, forceUpdate] = useState();

  useEffect(() => {
    setTimeout(forceUpdate, 2000);
  }, []);

  return <div>{Date.now()}</div>;
}

ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • Ok but why React has introduced this.forceUpdate(); in first place when component re-renders with setState in earlier versions ? – Hemadri Dasari Nov 08 '18 at 20:19
  • 1
    @Think-Twice I have personally never used it, and I can't think of a good use case for it right now, but I guess it's an escape hatch for those really special use cases. ["Normally you should try to avoid all uses of `forceUpdate()` and only read from `this.props` and `this.state` in `render()`."](https://reactjs.org/docs/react-component.html#forceupdate) – Tholle Nov 08 '18 at 20:21
  • 1
    Agreed. I even never used that in my experience but I know how it works so just trying to understand how the same thing can be done in hooks – Hemadri Dasari Nov 09 '18 at 02:29
  • 1
    @Tholle `and I can't think of a good use case for it right now` I have one, how about if state is not controlled by React. I don't use Redux, but I'm assuming it must do some sort of forceupdate. I personally use proxy's to maintain state, the component can then check for prop changes and then update. Seems to work very efficiently too. Eg., all my components are then controlled by a proxy, this proxy is backed by SessionStorage, so even if the user refreshes his webpage, state of even dropdowns etc, are maintained. IOW: I don't use state at all, everything is controlled with props. – Keith Dec 11 '18 at 10:39
  • @Tholle ok but if I want to rerender component and don't have possibility to pass changed props from parent component? – Matley Aug 30 '23 at 10:15
14

Simple code

const forceUpdate = React.useReducer(bool => !bool, true)[1];

Use:

forceUpdate();
GilCarvalhoDev
  • 575
  • 6
  • 11
  • 1
    One should probably provide an initialization value to avoid a TypeScript error (e.g. `useReducer(bool => !bool, true)[1]`). But otherwise … thumbs up for this solution! I find this solution so much nicer than the [example from the official docs](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate) which potentially increments a useless state value to infinity. – Philzen Jul 08 '22 at 05:16
9

Potential option is to force update only on specific component using key. Updating the key trigger a rendering of the component (which failed to update before)

For example:

const [tableKey, setTableKey] = useState(1);
...

useEffect(() => {
    ...
    setTableKey(tableKey + 1);
}, [tableData]);

...
<DataTable
    key={tableKey}
    data={tableData}/>
Idan
  • 5,405
  • 7
  • 35
  • 52
6

You can (ab)use normal hooks to force a rerender by taking advantage of the fact that React doesn't print booleans in JSX code

// create a hook
const [forceRerender, setForceRerender] = React.useState(true);

// ...put this line where you want to force a rerender
setForceRerender(!forceRerender);

// ...make sure that {forceRerender} is "visible" in your js code
// ({forceRerender} will not actually be visible since booleans are
// not printed, but updating its value will nonetheless force a
// rerender)
return (
  <div>{forceRerender}</div>
)

Fergie
  • 5,933
  • 7
  • 38
  • 42
  • 1
    In this case, when setBoolean changes twice children React.useEffect might not recoginze the update. – alma Sep 11 '19 at 02:43
  • As far as I understand it React treats every boolean update as a reason to rerender the page even if the boolean switches back again quickly. That said, React is of course not a standard, and exactly how it works in this instance is undefined and subject to change. – Fergie Sep 11 '19 at 08:00
  • 1
    I don't know. For some reason I'm attracted to this version. It tickles me :-). Besides which, it feel pure. Something changed that my JSX depends upon, so I rerender. It's invisibility doesn't detract from that IMO. – Adrian Bartholomew Aug 04 '20 at 15:17
5

react-tidy has a custom hook just for doing that called useRefresh:

import React from 'react'
import {useRefresh} from 'react-tidy'

function App() {
  const refresh = useRefresh()
  return (
    <p>
      The time is {new Date()} <button onClick={refresh}>Refresh</button>
    </p>
  )
}

Learn more about this hook

Disclaimer I am the writer of this library.

webNeat
  • 2,768
  • 1
  • 20
  • 22
4

My variation of forceUpdate is not via a counter but rather via an object:

// Emulates `forceUpdate()`
const [unusedState, setUnusedState] = useState()
const forceUpdate = useCallback(() => setUnusedState({}), [])

Because {} !== {} every time.

catamphetamine
  • 4,489
  • 30
  • 25
  • What is `useCallback()`? Where did that come from? oops. [I see it now...](https://reactjs.org/docs/hooks-reference.html#usecallback) – zipzit Mar 27 '20 at 23:17
4

One line solution:

const useForceUpdate = () => useState()[1];

useState returns a pair of values: the current state and a function that updates it - state and setter, here we are using only the setter in order to force re-render.

arielhad
  • 1,753
  • 15
  • 12
3

Solution in one single line:

const [,forceRender] = useReducer((s) => s+1, 0)

You can learn about useReducer here. https://reactjs.org/docs/hooks-reference.html#usereducer

think-serious
  • 1,229
  • 2
  • 12
  • 27
3
const useForceRender = () => {
  const [, forceRender] = useReducer(x => !x, true)
  return forceRender
}

Usage

function Component () {
  const forceRender = useForceRender() 
  useEffect(() => {
    // ...
    forceRender()
  }, [])
david_adler
  • 9,690
  • 6
  • 57
  • 97
2

This will render depending components 3 times (arrays with equal elements aren't equal):

const [msg, setMsg] = useState([""])

setMsg(["test"])
setMsg(["test"])
setMsg(["test"])
Janek Olszak
  • 4,067
  • 1
  • 28
  • 22
  • 1
    I believe you don't even need to put an item in the array. An empty array doesn't strictly equal to another empty array simply by different reference, just like objects. – Qwerty Feb 05 '20 at 13:37
  • yep, just wanted to show that as a way of passing data – Janek Olszak Feb 06 '20 at 14:20
1

For regular React Class based components, refer to React Docs for the forceUpdate api at this URL. The docs mention that:

Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render()

However, it is also mentioned in the docs that:

If your render() method depends on some other data, you can tell React that the component needs re-rendering by calling forceUpdate().

So, although use cases for using forceUpdate might be rare, and I have not used it ever, however I have seen it used by other developers in some legacy corporate projects that I have worked on.

So, for the equivalent functionality for Functional Components, refer to the React Docs for HOOKS at this URL. Per the above URL, one can use the "useReducer" hook to provide a forceUpdate functionality for Functional Components.

A working code sample that does not use state or props is provided below, which is also available on CodeSandbox at this URL

import React, { useReducer, useRef } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  // Use the useRef hook to store a mutable value inside a functional component for the counter
  let countref = useRef(0);

  const [, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    countref.current++;
    console.log("Count = ", countref.current);
    forceUpdate(); // If you comment this out, the date and count in the screen will not be updated
  }

  return (
    <div className="App">
      <h1> {new Date().toLocaleString()} </h1>
      <h2>You clicked {countref.current} times</h2>
      <button
        onClick={() => {
          handleClick();
        }}
      >
        ClickToUpdateDateAndCount
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

NOTE: An alternate approach using the useState hook (instead of useReducer) is also available at this URL.

Alan C. S.
  • 14,718
  • 2
  • 20
  • 16
1

There are many ways to force re-render in Hook.

For me simple way with useState() and tip of reference object values.

const [, forceRender] = useState({});

// Anywhre
forceRender({});

Codesandbox Example

Nghiệp
  • 4,038
  • 2
  • 23
  • 27
0

A bit late to the party but I notice that most (all) of the answers have missed the part where you can pass a callback to forceUpdate lifecycle method.

As per the react source code, this callback has the same behavior as the one in the setState method - it is executed after the update.

Hence, the most correct implementation would be like this:

    /**
 * Increments the state which causes a rerender and executes a callback
 * @param {function} callback - callback to execute after state update
 * @returns {function}
 */
export const useForceUpdate = (callback) => {
    const [state, updater] = useReducer((x) => x + 1, 0);

    useEffect(() => {
        callback && callback();
    }, [state]);

    return useCallback(() => {
        updater();
    }, []);
};
0

I was working with an array and spotted this issue. However, instead of explicit forceUpdate I found another approach - to deconstruct an array and set a new value for it using this code:

    setRoutes(arr => [...arr, newRoute]); // add new elements to the array
    setRouteErrors(routeErrs => [...routeErrs]); // the array elements were changed

I found it very interesting that setting even a copy of an array will not trigger the hook. I assume React does the shallow comparison

ryzhman
  • 674
  • 10
  • 22