5

Is there a replacement for forceUpdate that one can use with React hooks?

My use case is a shared state between component instances. Essentially each instance is just an view into single global state. With ES6 class the code register/unregister the component with the state in componentDidMount, the state calls forceUpdate on its changes and then the render method query the global state directly.

I consider to switch this to hooks. Initially useEffect seemed like a perfect match for the problem. But then I realized that I did not have access to forceUpdate.

I considered a workaround that called the useState hook to add a dummy counter and then updated the counter from the effect hook, but it felt like a hack. I can also copy all relevant shared state parameters into the component state in the effect hook and then refer to those from the state. But it essentially duplicates the state for no good bloating the code with lines that copy the state when currently I can just use those lines directly in the render method.

Update: Here is codesandbox.io link with shared state example that uses class components that I would like to convert to hooks without duplicating the state or using fake state updates.

Igor Bukanov
  • 4,636
  • 3
  • 16
  • 23
  • It would be very helpful, if you reproduce the problem in a https://codesandbox.io – Mohamed Ramrami Sep 11 '19 at 09:30
  • 1
    Components rerender when state **or** props update... have you tried passing a "data" prop to "force" a rerender (similar to how react-virtualize works)? – Drew Reese Sep 11 '19 at 14:41
  • 1
    The values that are used in the render function are internal to the component. Passing them as properties means that the parent needs to know about this implementation details and essentially duplicates the single external state among all instances complicating the implementation for no benefits. – Igor Bukanov Sep 11 '19 at 16:53

3 Answers3

9

There is one other hack you can use to do a forceUpdate. Here it is:

Edit kind-hooks-rx2h4

From react docs:

Both useState and useReducer Hooks bail out of updates if the next value is the same as the previous one. Mutating state in place and calling setState will not cause a re-render. Normally, you shouldn’t mutate local state in React. However, as an escape hatch, you can use an incrementing counter to force a re-render even if the state has not changed.

Praneeth Paruchuri
  • 1,012
  • 3
  • 12
2

You can simply do this

You can simply add a dummy state that you can change to reliably initiate a re-render.

const [rerender, setRerender] = useState(false);

...
setRerender(!rerender); //whenever you want to re-render

And this will ensure a re-render of the component, since components always re-render on state change

Abraham
  • 12,140
  • 4
  • 56
  • 92
0

As a workaround I ended up with the following hook for a functional component:

function useForceUpdate() {
  const [forcedUpdateCounter, setState] = useState(0);
  return () => setState(forcedUpdateCounter + 1);
}

and use it like:

function ExampleWithSharedState(props) {
  const forceUpdate = useForceUpdate();
  useEffect(() => {
    SharedState.addObserver(forceUpdate);
    return () => {
      SharedState.removeObserver(forceUpdate);
    };
  });
  return <div>Hello, {SharedState.getValue()}</div>
}
Igor Bukanov
  • 4,636
  • 3
  • 16
  • 23