TLDR
Is there a way to force React to recreate a component (functional component using hooks) from scratch instead of re-rendering it on a prop
change?
EDIT
The key attribute DOES work. The problem I was having was due to the state update sequence that I was doing. The first update was trying immediately to render a component that needed the last update to be already done.
I'm working on a component called <AddProductWidget>
, that will get the following props
about a product and need to display its details. It belongs to a page with a search bar that you fill the productID and it gets the product details from a cloud function.
It's rendered like this:
<AddProductWidget
userImages={props.productDetails.userImages} // Array of strings (urls)
title={props.productDetails.title}
stars={props.productDetails.stars}
/>
AddProductWidget.js
It renders a <UserImages>
component that lets the user clicks on some images to select before saving to the database. So it needed a state
to 'remember' which images have been clicked by the user.
function AddProductWidget(props) {
const [userImagesSelected, setUserImagesSelected] = useState(
() => {
if (props.userImages && props.userImages > 0) {
return new Array(props.userImages.length).fill(false);
}
return null;
}
);
// IT renders the <UserImages> component
// The code here is simplified
return(
<UserImages
userImages={props.userImages}
userImagesSelected={userImagesSelected}
setUserImagesSelected={setUserImagesSelected}
/>
);
}
The initial state of that array userImagesSelected
should be the same length of the userImages
array and be filled with false
values, that will become true
once the user start clicking on the images. If the product doesn't have user images, props.userImages
will be null
and userImagesSelected
should also be null
.
Everything is working as expected when I load the first product. Whether it has images or not, everything is being displayed correctly.
The problem:
The problem is when I load a product with zero images, and subsequentely I load another product with 5 images, for example. Basically the props
for AddProductWidget
will change (since it received a new product), but because React seems to be trying to use the same component instance, I get an error, because my UserImages
component will try to render the selected images information, but the state of userImagesSelected
will still be null
, because the last products had no images.
When I was working with classes, I could force React to recreate a component instance using the key
attribute, but that doesn't seem to work with hooks, since the following code didn't solve my problem:
<AddProductWidget
key={productID}
userImages={props.productDetails.userImages} // Array of strings (urls)
title={props.productDetails.title}
stars={props.productDetails.stars}
/>
Question
Is there a way to force React to recreate a component (functional component using hooks) from scratch instead of re-rendering it on a prop
change?
Extra
A found a way around it, but it seems a bit hacky, even though it's on React docs. It basically uses a state to detect when props
change and calls setState()
during render. I would like it better to recreate my component from scratch.
Hooks FAQ: How do I implement getDerivedStateFromProps
Not sure how you decided that a different key
won't re-mount the component but it should do that (are you sure the key has changed?)
Here is a running example showing this
function Test() {
React.useEffect(() => {
console.log('Test was mounted');
return () => {
console.log('Test was un-mounted');
};
}, []);
return "hi there, I'm Test";
}
function App() {
const [val, setVal] = React.useState("Change me...");
return (
<div className="App">
<input value={val} onChange={e => setVal(e.target.value)} />
<br />
<Test key={val} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With