Reputation: 901
Question: I am loading data from an API into my functional app component with useEffect (example below). I can't understand how to then set the variable activeToken
because phrases
is not yet defined, perhaps because useEffect is async?
What pattern should I be using to ensure that phrases
exists before I invoke activeToken
?
function App() {
const getActiveToken = (tokens) => {
for(let i = 0; i < tokens.length; i++) {
if (tokens[i].mask === true) {
return i
}
}
}
useEffect(() => {
fetch('http://localhost:5000/api/v1')
.then((response) => response.json())
.then((data) => setPhrases(data))
}
)
const [termIndex, setTermIndex] = useState(0);
const [phrases, setPhrases] = useState([]);
const [activeToken, setActiveToken] = useState(getActiveToken(phrases[termIndex].tokens))}
Upvotes: 0
Views: 437
Reputation: 26608
Try this dirction: Try the following:
function App() {
const [activeToken, setActiveToken] = useState() // activeToken can't be set in the initial run anyway
useEffect(() => {
fetch('http://localhost:5000/api/v1')
.then((response) => response.json())
.then((tokens) => {
for(let i = 0; i < tokens.length; i++) {
if (tokens[i].mask === true) {
setActiveToken(tokens[i])
}
}
})
}
Upvotes: 0
Reputation: 193318
You are trying to set the initial state of activeToken
from a state that you would update asynchronously (phases
). Since phases
is not ready when the component mounts, it won't be updated when phases
changes, because the initial value is only used at the 1st render (useState
initialization).
Since activeToken
is derived from the tokens
, which is derived from phases
, you can compute it in the body of the component, and wrap it with useMemo
, so it would only be computed when tokens
change, but it's not strictly necessary unless tokens
is huge. In addition the expression phrases[termIndex].tokens
would throw an error, because phrases[termIndex]
is undefined
.
const getActiveToken = (tokens = []) => {
for(let i = 0; i < tokens.length; i++) {
if (tokens[i].mask === true) {
return i
}
}
}
function App() {
useEffect(() => {
fetch('http://localhost:5000/api/v1')
.then((response) => response.json())
.then((data) => setPhrases(data))
});
const [termIndex, setTermIndex] = useState(0);
const [phrases, setPhrases] = useState([]);
const tokens = phrases[termIndex]?.tokens;
const activeToken = useMemo(() => getActiveToken(tokens), [tokens]);
Upvotes: 1
Reputation: 2571
Make a seperate async
function containing async code and call it in useEffect
.
//seperate function
const fetcher = async () => {
fetch('http://localhost:5000/api/v1')
.then((response) => response.json())
.then((data) => setPhrases(data))
}
useEffect(() => {
fetcher();
}
You can also
useEffect(() => {
//within useEffect
let fetcher = async () => {
fetch('http://localhost:5000/api/v1')
.then((response) => response.json())
.then((data) => setPhrases(data))
}
fetcher();
}
Upvotes: 0