I have the below code where I want to create a list of tags. In this example I'm fetching a list of tags in setAllTags()
and then a number of available tags in setAvailableTags()
.
Then problem that I have is that when setAvailableTags()
is run it will remove the tags that was fetched in setAllTags()
. It seems like the state that I set in setAllTags()
is not used when setAvailableTags()
is settings it's state.
Any idea what I can do to fix this?
https://codesandbox.io/s/rj40lz6554
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Search = () => {
const [tags, setTags] = useState({
all: [],
available: []
});
const setAllTags = () => {
const all = ["tag 1", "tag 2", "tag 3"];
const newValue = {
...tags,
all
};
console.log("setting all tags to ", newValue);
setTags(newValue);
};
const setAvailableTags = () => {
const available = ["tag 1", "tag 2"];
const newValue = {
...tags,
available
};
console.log("setting available tags to", newValue);
setTags(newValue);
};
useEffect(setAllTags, []);
useEffect(setAvailableTags, []);
return (
<div>
<div>
<select placeholder="Tags">
{tags.all.map((tag, i) => (
<option key={tag + i} value={tag}>
{tag}
</option>
))}
</select>
</div>
</div>
);
};
const App = () => {
return (
<div>
<h1>Hello React!</h1>
<Search />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
The console is logged with
setting all tags to Object {all: Array[3], available: Array[0]}
setting available tags to Object {all: Array[0], available: Array[2]}
In the react docs, you can see how useEffect
works (https://reactjs.org/docs/hooks-effect.html)
Experienced JavaScript developers might notice that the function passed to useEffect is going to be different on every render. This is intentional. In fact, this is what lets us read the count value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a different effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result — each effect “belongs” to a particular render. We will see more clearly why this is useful later on this page.
What this means, is that each side effect within your render function will only have access to the initial result of useState
. Remember that react states are immutable, so it doesn't make sense for you to update the state, and then try and use the updated version within the same render cycle.
You can see this by simply calling:
setTags({ all: ['test'] })
console.log(tags)
You should notice that tags
does not change at all.
Personally I would use hook conventions here, and separate your useState out into two separate variables:
const [allTags, setAllTags] = useState([])
const [availableTags, setAvailableTags] = useState([])
This makes your effects more explicit (as they only need to update the one variable), and improves readability.
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