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()
.
This question is related to
javascript
reactjs
Using react hooks, you can now call useState()
in your function component.
useState()
will return an array of 2 things:
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 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>
);
}
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.
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.
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(()=>{
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)
}
}
Update react v16.8 (16 Feb 2019 realease)
Since react 16.8 released with hooks, function components are now have the ability to hold persistent state
. With that ability you can now mimic a forceUpdate
:
function App() {_x000D_
const [, updateState] = React.useState();_x000D_
const forceUpdate = React.useCallback(() => updateState({}), []);_x000D_
console.log("render");_x000D_
return (_x000D_
<div>_x000D_
<button onClick={forceUpdate}>Force Render</button>_x000D_
</div>_x000D_
);_x000D_
}_x000D_
_x000D_
const rootElement = document.getElementById("root");_x000D_
ReactDOM.render(<App />, rootElement);
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>_x000D_
<div id="root"/>
_x000D_
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.
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>
)
}
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} />;
};
Official FAQ ( https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate ) now recommends this way if you really need to do it:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
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);
Disclaimer: NOT AN ANSWER TO THE PROBLEM.
Leaving an Important note here:
If you are trying to forceupdate a stateless component, chances are there is something wrong with your design.
Consider the following cases:
Source: Stackoverflow.com