How to force a functional React component to render?

444.5k views Asked by At

I have a function component, and I want to force it to re-render.

How can I do so?
Since there's no instance this, I cannot call this.forceUpdate().

13

There are 13 answers

27
Yairopro On BEST ANSWER

You can now, using React hooks

using useReducer (short answer)

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

From React FAQ

How to use:

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

  return (
    <div onClick={forceUpdate}>
      Click me to refresh
    </div>
  );
}

Using useState (more explicit answer)

Using react hooks, you can now call useState() in your function component.

useState() will return an array of 2 things:

  1. A value, representing the current state.
  2. Its setter. Use it to update the value.

Updating the value by its setter will force your function component to re-render,
just like forceUpdate does:

import React, { useState } from 'react';

//create your forceUpdate hook
function useForceUpdate(){
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => value + 1); // update state to force render
    // A function that increment  the previous state like here 
    // is better than directly setting `setValue(value + 1)`
}

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>
    );
}

You can find a demo here.

The component above uses a custom hook function (useForceUpdate) which uses the react state hook useState. It increments the component's state's value and thus tells React to re-render the component.


EDIT

In an old version of this answer, the snippet used a boolean value, and toggled it in forceUpdate(). Now that I've edited my answer, the snippet use a number rather than a boolean.

Why ? (you would ask me)

Because once it happened to me that my forceUpdate() was called twice subsequently from 2 different events, and thus it was reseting the boolean value at its original state, and the component never rendered.

This is because in the useState's setter (setValue here), React compare the previous state with the new one, and render only if the state is different.

2
Sagiv b.g On

Update react v16.8 (16 Feb 2019 realease)

Since react 16.8 released with hooks, function components have the ability to hold persistent state. With that ability you can now mimic a forceUpdate:

function App() {
  const [, forceUpdate] = React.useReducer(o => !o);
  console.log("render");
  return (
    <div>
      <button onClick={forceUpdate}>Force Render</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"/>

Note that this approach should be re-considered and in most cases when you need to force an update you probably doing something wrong.


Before react 16.8.0

No you can't, State-Less function components are just normal functions that returns jsx, you don't have any access to the React life cycle methods as you are not extending from the React.Component.

Think of function-component as the render method part of the class components.

0
Neel Dsouza On

If you are using functional components with version < 16.8. One workaround would be to directly call the same function like

import React from 'react';

function MyComponent() {
    const forceUpdate = MyComponent();
    
    return (
        <div>
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

But this will break if you were passing some prop to it. In my case i just passed the same props which I received to rerender function.

0
Charles Goodwin On

This can be done without explicitly using hooks provided you add a prop to your component and a state to the stateless component's parent component:

const ParentComponent = props => {
  const [updateNow, setUpdateNow] = useState(true)

  const updateFunc = () => {
    setUpdateNow(!updateNow)
  }

  const MyComponent = props => {
    return (<div> .... </div>)
  }

  const MyButtonComponent = props => {
    return (<div> <input type="button" onClick={props.updateFunc} />.... </div>)
  }

  return (
    <div> 
      <MyComponent updateMe={updateNow} />
      <MyButtonComponent updateFunc={updateFunc}/>
    </div>
  )
}
2
Gramotei On

Official FAQ now recommends this way if you really need to do it:

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

  function handleClick() {
    forceUpdate();
  }
0
Arshal Daison On

If you already have a state inside the function component and you don't want to alter it and requires a re-render you could fake a state update which will, in turn, re-render the component

const [items,setItems] = useState({
   name:'Your Name',
   status: 'Idle'
})
const reRender = () =>{
setItems((state) => [...state])
}

this will keep the state as it was and will make react into thinking the state has been updated

0
gentlee On

Best approach - no excess variables re-created on each render:

const forceUpdateReducer = (i) => i + 1

export const useForceUpdate = () => {
  const [, forceUpdate] = useReducer(forceUpdateReducer, 0)
  return forceUpdate
}

Usage:

const forceUpdate = useForceUpdate()

forceUpdate()
0
Ioan Stoianov On

For me just updating the state didn't work. I am using a library with components and it looks like I can't force the component to update.

My approach is extending the ones above with conditional rendering. In my case, I want to resize my component when a value is changed.

//hook to force updating the component on specific change
const useUpdateOnChange = (change: unknown): boolean => {
  const [update, setUpdate] = useState(false);

  useEffect(() => {
    setUpdate(!update);
  }, [change]);

  useEffect(() => {
    if (!update) setUpdate(true);
  }, [update]);

  return update;
};

const MyComponent = () => {
  const [myState, setMyState] = useState();
  const update = useUpdateOnChange(myState);

  ...

  return (
    <div>
      ... ...
      {update && <LibraryComponent />}
    </div>
  );
};

You need to pass the value you want to track for change. The hook returns boolean which should be used for conditional rendering.

When the change value triggers the useEffect update goes to false which hides the component. After that the second useEffect is triggered and update goes true which makes the component visible again and this results in updating (resizing in my case).

4
Abraham On

Simplest way

if you want to force a re-render, add a dummy state you can change to initiate a re-render.

const rerender = useState();

...
setRerender(Symbol());     //built in Symbol() always returns unique value

And this will ensure a re-render, And you can call setRerender(Symbol()) anywhere, whenever you want :)

0
Manohar Reddy Poreddy On

The accepted answer is good. Just to make it easier to understand.

Example component:

export default function MyComponent(props) {

    const [updateView, setUpdateView] = useState(0);

    return (
        <>
            <span style={{ display: "none" }}>{updateView}</span>
        </>
    );
}

To force re-rendering call the code below:

setUpdateView((updateView) => ++updateView);
2
danieltan95 On

None of these gave me a satisfactory answer so in the end I got what I wanted with the key prop, useRef and some random id generator like shortid.

Basically, I wanted some chat application to play itself out the first time someone opens the app. So, I needed full control over when and what the answers are updated with the ease of async await.

Example code:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// ... your JSX functional component, import shortid somewhere

const [render, rerender] = useState(shortid.generate())

const messageList = useRef([
    new Message({id: 1, message: "Hi, let's get started!"})
])

useEffect(()=>{
    async function _ () {
      await sleep(500)
      messageList.current.push(new Message({id: 1, message: "What's your name?"}))
      // ... more stuff
      // now trigger the update
      rerender(shortid.generate())
   } 
   _()
}, [])

// only the component with the right render key will update itself, the others will stay as is and won't rerender.
return <div key={render}>{messageList.current}</div> 

In fact this also allowed me to roll something like a chat message with a rolling .

const waitChat = async (ms) => {
    let text = "."
    for (let i = 0; i < ms; i += 200) {
        if (messageList.current[messageList.current.length - 1].id === 100) {
            messageList.current = messageList.current.filter(({id}) => id !== 100)
        }
        messageList.current.push(new Message({
            id: 100,
            message: text
        }))
        if (text.length === 3) {
            text = "."
        } else {
            text += "."
        }
        rerender(shortid.generate())
        await sleep(200)
    }
    if (messageList.current[messageList.current.length - 1].id === 100) {
        messageList.current = messageList.current.filter(({id}) => id !== 100)
    }
}
0
uma mahesh On

BEST WAY TO KEEP CONSOLE LOG AND CHECK WHETHER THE USE EFFECT HOOK is TRIGGERING OR NOT ON EACH TIME WHEN WE SET

const [multiSelectList, setMultiSelectList] = useState<string[]>();
    
    setMultiSelectList(multiDropDownValues);
    
      useEffect(()=>{
          console.log("triggers from combo multi on multiSelectList changes"+JSON.stringify(multiSelectList));
        },[multiSelectList]);
1
Charith Jayasanka On

I used a third party library called use-force-update to force render my react functional components. Worked like charm. Just use import the package in your project and use like this.

import useForceUpdate from 'use-force-update';

const MyButton = () => {

  const forceUpdate = useForceUpdate();

  const handleClick = () => {
    alert('I will re-render now.');
    forceUpdate();
  };

  return <button onClick={handleClick} />;
};