837
votes

I have an external (to the component), observable object that I want to listen for changes on. When the object is updated it emits change events, and then I want to rerender the component when any change is detected.

With a top-level React.render this has been possible, but within a component it doesn't work (which makes some sense since the render method just returns an object).

Here's a code example:

export default class MyComponent extends React.Component {

  handleButtonClick() {
    this.render();
  }

  render() {
    return (
      <div>
        {Math.random()}
        <button onClick={this.handleButtonClick.bind(this)}>
          Click me
        </button>
      </div>
    )
  }
}

Clicking the button internally calls this.render(), but that's not what actually causes the rendering to happen (you can see this in action because the text created by {Math.random()} doesn't change). However, if I simply call this.setState() instead of this.render(), it works fine.

So I guess my question is: do React components need to have state in order to rerender? Is there a way to force the component to update on demand without changing the state?

20
the accepted answer says this.forceUpdate() is the right solution whereas rest of all answers and several comments are against using forceUpdate(). Will that then be fine to say that the question didn't get a proper solution/answer yet?Adil
The excepted answer answered my question at the time. Is is technically the answer I was looking for, and I still think the right answer. The other answers I think are good supplemental information for folks with the same question to be aware of.Philip Walton
Interesting to note is that you DON'T NEED ANYTHING IN STATE at all other than initializing it to a plain object, then calling this.setState({}) just triggers a new render. React is great but also weird sometimes. Therefore you could directly loop through a stores data when trigger a change without extra plumbing or worry about data per component instance.King Friday
On the whole I would say yes. If you are using force update this is for updating components where they may be dependent on changes outside of the state management of your app. I cannot think of a good example of that. Useful to know though.Daniel

20 Answers

895
votes

In class components, you can call this.forceUpdate() to force a rerender.

Documentation: https://facebook.github.io/react/docs/component-api.html

In function components, there's no equivalent of forceUpdate, but you can contrive a way to force updates with the useState hook.

382
votes

forceUpdate should be avoided because it deviates from a React mindset. The React docs cite an example of when forceUpdate might be used:

By default, when your component's state or props change, your component will re-render. However, if these change implicitly (eg: data deep within an object changes without changing the object itself) or if your render() method depends on some other data, you can tell React that it needs to re-run render() by calling forceUpdate().

However, I'd like to propose the idea that even with deeply nested objects, forceUpdate is unnecessary. By using an immutable data source tracking changes becomes cheap; a change will always result in a new object so we only need to check if the reference to the object has changed. You can use the library Immutable JS to implement immutable data objects into your app.

Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.forceUpdate()

Changing the key of the element you want re-rendered will work. Set the key prop on your element via state and then when you want to update set state to have a new key.

<Element key={this.state.key} /> 

Then a change occurs and you reset the key

this.setState({ key: Math.random() });

I want to note that this will replace the element that the key is changing on. An example of where this could be useful is when you have a file input field that you would like to reset after an image upload.

While the true answer to the OP's question would be forceUpdate() I have found this solution helpful in different situations. I also want to note that if you find yourself using forceUpdate you may want to review your code and see if there is another way to do things.

NOTE 1-9-2019:

The above (changing the key) will completely replace the element. If you find yourself updating the key to make changes happen you probably have an issue somewhere else in your code. Using Math.random() in key will re-create the element with each render. I would NOT recommend updating the key like this as react uses the key to determine the best way to re-render things.

104
votes

Actually, forceUpdate() is the only correct solution as setState() might not trigger a re-render if additional logic is implemented in shouldComponentUpdate() or when it simply returns false.

forceUpdate()

Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate(). more...

setState()

setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). more...


forceUpdate()this.forceUpdate()


Hooks: How can I force component to re-render with hooks in React?


BTW: Are you mutating state or your nested properties don't propagate?

42
votes

I Avoided forceUpdate by doing following

WRONG WAY : do not use index as key

this.state.rows.map((item, index) =>
   <MyComponent cell={item} key={index} />
)

CORRECT WAY : Use data id as key, it can be some guid etc

this.state.rows.map((item) =>
   <MyComponent item={item} key={item.id} />
)

so by doing such code improvement your component will be UNIQUE and render naturally

38
votes

When you want two React components to communicate, which are not bound by a relationship (parent-child), it is advisable to use Flux or similar architectures.

What you want to do is to listen for changes of the observable component store, which holds the model and its interface, and saving the data that causes the render to change as state in MyComponent. When the store pushes the new data, you change the state of your component, which automatically triggers the render.

Normally you should try to avoid using forceUpdate() . From the documentation:

Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your application much simpler and more efficient

22
votes

use hooks or HOC take your pick

Using hooks or the HOC (higher order component) pattern, you can have automatic updates when your stores change. This is a very light-weight approach without a framework.

useStore Hooks way to handle store updates

interface ISimpleStore {
  on: (ev: string, fn: () => void) => void;
  off: (ev: string, fn: () => void) => void;
}

export default function useStore<T extends ISimpleStore>(store: T) {
  const [storeState, setStoreState] = useState({store});
  useEffect(() => {
    const onChange = () => {
      setStoreState({store});
    }
    store.on('change', onChange);
    return () => {
      store.off('change', onChange);
    }
  }, []);
  return storeState.store;
}

withStores HOC handle store updates

export default function (...stores: SimpleStore[]) {
  return function (WrappedComponent: React.ComponentType<any>) {
    return class WithStore extends PureComponent<{}, {lastUpdated: number}> {
      constructor(props: React.ComponentProps<any>) {
        super(props);
        this.state = {
          lastUpdated: Date.now(),
        };
        this.stores = stores;
      }

      private stores?: SimpleStore[];

      private onChange = () => {
        this.setState({lastUpdated: Date.now()});
      };

      componentDidMount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            // each store has a common change event to subscribe to
            store.on('change', this.onChange);
          });
      };

      componentWillUnmount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            store.off('change', this.onChange);
          });
      };

      render() {
        return (
          <WrappedComponent
            lastUpdated={this.state.lastUpdated}
            {...this.props}
          />
        );
      }
    };
  };
}

SimpleStore class

import AsyncStorage from '@react-native-community/async-storage';
import ee, {Emitter} from 'event-emitter';

interface SimpleStoreArgs {
  key?: string;
  defaultState?: {[key: string]: any};
}

export default class SimpleStore {
  constructor({key, defaultState}: SimpleStoreArgs) {
    if (key) {
      this.key = key;
      // hydrate here if you want w/ localState or AsyncStorage
    }
    if (defaultState) {
      this._state = {...defaultState, loaded: false};
    } else {
      this._state = {loaded: true};
    }
  }
  protected key: string = '';
  protected _state: {[key: string]: any} = {};
  protected eventEmitter: Emitter = ee({});
  public setState(newState: {[key: string]: any}) {
    this._state = {...this._state, ...newState};
    this.eventEmitter.emit('change');
    if (this.key) {
      // store on client w/ localState or AsyncStorage
    }
  }
  public get state() {
    return this._state;
  }
  public on(ev: string, fn:() => void) {
    this.eventEmitter.on(ev, fn);
  }
  public off(ev: string, fn:() => void) {
    this.eventEmitter.off(ev, fn);
  }
  public get loaded(): boolean {
    return !!this._state.loaded;
  }
}

How to Use

In the case of hooks:

// use inside function like so
const someState = useStore(myStore);
someState.myProp = 'something';

In the case of HOC:

// inside your code get/set your store and stuff just updates
const val = myStore.myProp;
myOtherStore.myProp = 'something';
// return your wrapped component like so
export default withStores(myStore)(MyComponent);

MAKE SURE To export your stores as a singleton to get the benefit of global change like so:

class MyStore extends SimpleStore {
  public get someProp() {
    return this._state.someProp || '';
  }
  public set someProp(value: string) {
    this.setState({...this._state, someProp: value});
  }
}
// this is a singleton
const myStore = new MyStore();
export {myStore};

This approach is pretty simple and works for me. I also work in large teams and use Redux and MobX and find those to be good as well but just a lot of boilerplate. I just personally like my own approach because I always hated a lot of code for something that can be simple when you need it to be.

19
votes

So I guess my question is: do React components need to have state in order to rerender? Is there a way to force the component to update on demand without changing the state?

The other answers have tried to illustrate how you could, but the point is that you shouldn't. Even the hacky solution of changing the key misses the point. The power of React is giving up control of manually managing when something should render, and instead just concerning yourself with how something should map on inputs. Then supply stream of inputs.

If you need to manually force re-render, you're almost certainly not doing something right.

19
votes

In 2021, this is the official way to forceUpdate a React Functional Component.

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

  function handleClick() {
    forceUpdate();
  }

I know the OP is for a class component. But the question was asked in 2015 and now that hooks are available, many may search for forceUpdate in functional components. This little bit is for them.

7
votes

There are a few ways to rerender your component:

The simplest solution is to use forceUpdate() method:

this.forceUpdate()

One more solution is to create not used key in the state(nonUsedKey) and call setState function with update of this nonUsedKey:

this.setState({ nonUsedKey: Date.now() } );

Or rewrite all current state:

this.setState(this.state);

Props changing also provides component rerender.

5
votes

For completeness, you can also achieve this in functional components:

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

Or, as a reusable hook:

const useForceUpdate = () => {
  const [, updateState] = useState();
  return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();

See: https://stackoverflow.com/a/53215514/2692307

Please note that using a force-update mechanism is still bad practice as it goes against the react mentality, so it should still be avoided if possible.

4
votes

You could do it a couple of ways:

1. Use the forceUpdate() method:

There are some glitches that may happen when using the forceUpdate() method. One example is that it ignores the shouldComponentUpdate() method and will re-render the view regardless of whether shouldComponentUpdate() returns false. Because of this using forceUpdate() should be avoided when at all possible.

2. Passing this.state to the setState() method

The following line of code overcomes the problem with the previous example:

this.setState(this.state);

Really all this is doing is overwriting the current state with the current state which triggers a re-rendering. This still isn't necessarily the best way to do things, but it does overcome some of the glitches you might encounter using the forceUpdate() method.

2
votes

We can use this.forceUpdate() as below.

       class MyComponent extends React.Component {



      handleButtonClick = ()=>{
          this.forceUpdate();
     }


 render() {

   return (
     <div>
      {Math.random()}
        <button  onClick={this.handleButtonClick}>
        Click me
        </button>
     </div>
    )
  }
}

 ReactDOM.render(<MyComponent /> , mountNode);

The Element 'Math.random' part in the DOM only gets updated even if you use the setState to re-render the component.

All the answers here are correct supplementing the question for understanding..as we know to re-render a component with out using setState({}) is by using the forceUpdate().

The above code runs with setState as below.

 class MyComponent extends React.Component {



             handleButtonClick = ()=>{
                this.setState({ });
              }


        render() {
         return (
  <div>
    {Math.random()}
    <button  onClick={this.handleButtonClick}>
      Click me
    </button>
  </div>
)
  }
 }

ReactDOM.render(<MyComponent /> , mountNode);
1
votes

Just another reply to back-up the accepted answer :-)

React discourages the use of forceUpdate() because they generally have a very "this is the only way of doing it" approach toward functional programming. This is fine in many cases, but many React developers come with an OO-background, and with that approach, it's perfectly OK to listen to an observable object.

And if you do, you probably know you MUST re-render when the observable "fires", and as so, you SHOULD use forceUpdate() and it's actually a plus that shouldComponentUpdate() is NOT involved here.

Tools like MobX, that takes an OO-approach, is actually doing this underneath the surface (actually MobX calls render() directly)

0
votes

I have found it best to avoid forceUpdate(). One way to force re-render is to add dependency of render() on a temporary external variable and change the value of that variable as and when needed.

Here's a code example:

class Example extends Component{
   constructor(props){
      this.state = {temp:0};

      this.forceChange = this.forceChange.bind(this);
   }

   forceChange(){
      this.setState(prevState => ({
          temp: prevState.temp++
      })); 
   }

   render(){
      return(
         <div>{this.state.temp &&
             <div>
                  ... add code here ...
             </div>}
         </div>
      )
   }
}

Call this.forceChange() when you need to force re-render.

0
votes

ES6 - I am including an example, which was helpful for me:

In a "short if statement" you can pass empty function like this:

isReady ? ()=>{} : onClick

This seems to be the shortest approach.

()=>{}
0
votes

forceUpdate(); method will work but it is advisable to use setState();

0
votes

In order to accomplish what you are describing please try this.forceUpdate().

0
votes

Another way is calling setState, AND preserve state:

this.setState(prevState=>({...prevState}));

0
votes

forceUpdate(), but every time I've ever heard someone talk about it, it's been followed up with you should never use this.

-3
votes

You can use forceUpdate() for more details check (forceUpdate()).