3

I'm learning redux and want to find out how useSelector updates a component, because the component doesn't have its own state.

I understand that useSelector() subscribes the component to the store, and when the store is updated, the component also updates.

Class components have this.forceUpdate(), but functional components don't have it.

How does one force-update a functional component?

simmer
  • 2,639
  • 1
  • 18
  • 22
Alex4answer
  • 485
  • 1
  • 5
  • 15
  • 1
    If useSelector didn't update the component you probably mutated the state in the reducer. – HMR Dec 14 '20 at 07:17

5 Answers5

1

You can simply do this

Add a dummy state that you can change to reliably initiate a re-render.

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

...
//And whenever you want to re-render, you can do this
setRerender(!rerender);

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

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

The react-redux package relies on the rendering engine of react/react-dom to trigger the re-render of a given component that uses the useSelector hook.

If you take a look at the source of useSelector you can notice the use of useReducer:

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

As the name (forceRender) implies, redux uses this to trigger a re-render by react.

With v8 of react-redux the implementation of this mechanism changes but still relies on react-hooks for the re-render.


If you are curious how React handles re-renders, take a look at this excellent SO answer. It provides a great entry on the implementation details of how react-hooks are associated with the calling component.

I don't repeat Ryan here, but to sum it up:

The renderer keeps a reference to the component that is currently rendered. All hooks being executed during this render (no matter how deeply nested in custom-hooks they are) ultimately belong to this component. So, the useReducer is associated with the component within which you called useSelector.

The dispatch function of useReducer triggers a re-render of this component (React either calls the render() method of a class-component or executes the function body of a functional component).


If you are curious how react-redux determines when it should force this re-render (by utilizing useReducer), take another look at the source code of useSelector.

Redux uses the subscriber-pattern to get notified of updates to the state. If the root-state of redux is updated the following things happen:

  1. useSelector hooks in your application re-run their selector function
  2. This re-selected state is compared to the previously selected state (by default via === comparison). The second argument to useSelector can be a comparison function to change this behavior
  3. If the re-selected state differs from the previously selected state, a re-render is triggered via the useReducer hook.

The subscriber pattern is very react-like but potentially helps save many re-renders. Calling several useSelector hooks is cheap when compared with re-renders.

MarcRo
  • 2,434
  • 1
  • 9
  • 24
1

Continuing on other answers, to keep your code clean you can create a dummy state and then set it in your own forceUpdate function:

    const [helper, setHelper] = useState(false);
    function forceUpdate(){
        setHelper(!helper);
    }

Now you can just call forceUpdate() in the rest of your code:

<div onClick={() => forceUpdate()} />
Flion
  • 10,468
  • 13
  • 48
  • 68
0

First of all, I want to mention that you don't need to do a force update when you use useSelector hook. Rerender will happen automatically whenever the selected state value will be updated.

But if you need to force update the functional component you can use this approach.

import React, { useState } from 'react';

//create your forceUpdate hook
function useForceUpdate(){
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => ++value); // update the state to force render
}

function MyComponent() {
    // call your hook here
    const forceUpdate = useForceUpdate();

    return (
        <div>
            {/*Clicking on the button will force to re-render like force update does */}
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

I highly recommend avoiding the use of this hack, in 99% of issues you can resolve them without force update. But in any case, it's good to know that there is such a possibility in the functional component exists too.

kyivcoder
  • 71
  • 3
  • yes, i know that useSelector update component, but i want to know, how it works – Alex4answer Dec 14 '20 at 08:59
  • 1
    When an action is dispatched to the Redux store, useSelector() only forces a re-render if the selector result appears to be different than the last result. More info about what happens under the hood you can find here: https://react-redux.js.org/api/hooks#equality-comparisons-and-updates – kyivcoder Dec 14 '20 at 09:10
0

Maybe something like this could help you:

In a Class Component you could pass a property like the one below...

<Element onSomethingHappen={
   ()=>{
   if(shouldComponentUpdate())
      this.forceUpdate();
}}/>

In the function component you can call the updater like this one:

function FunctionComponent(props){
    //When you need it you can update like this one...
    props.onSomethingHappen(); 
    // Here you are ;) let me know if this helps you
}