I was wondering how it is possible to run a function after you use the useEffect to fetch data, where the function is manipulating the data after its been pulled?
import React, { useState, useEffect } from 'react';
const Result = (props) => {
const [ playerName, setPlayerName ] = useState('');
const [ playerChoice, setPlayerChoice ] = useState(null);
const [ puterChoice, setComputerChoice ] = useState(null);
const [ result, setResult ] = useState(null);
useEffect(() => {
setPlayerName(props.location.state.playerName);
setPlayerChoice(props.location.state.playerChoice);
setComputerChoice(generateComputerChoice);
setResult(getResult())
}, []);
const getResult = () => {
// code that runs after the setting of the playerName and playerChoice. Will return "Win", "Lose", or "Draw"
};
const generateComputerChoice = () => {
const outes = [ 'Rock', 'Paper', 'Scissors' ];
return outes[Math.floor(Math.random() * outes.length)];
};
return (
<div className="app-container">
<strong>YOU {result}</strong>
<br />
<strong>{playerName}</strong> chose <strong>{playerChoice}</strong>
<br />
<strong>Computer</strong> chose <strong>{puterChoice}</strong>
</div>
);
};
export default Result;
So here in this example I grab the playerName
and playerChoice
from a previous page, and then add that to my useState on page load.
After that I randomly generate the puterChoice
.
However, after that I want to use the playerChoice
amd puterChoice
that has been added to the state and use that to see if the game is a win, lose or draw.
result
ends up being null
because I assume that by the time the getResult
function is called, the states haven't been set yet.
Do you guys know what should be done in this situation? Seems like this could be a mon thing considering you might wanna grab data from an API and then do something with that data before wanting to render it.
I was wondering how it is possible to run a function after you use the useEffect to fetch data, where the function is manipulating the data after its been pulled?
import React, { useState, useEffect } from 'react';
const Result = (props) => {
const [ playerName, setPlayerName ] = useState('');
const [ playerChoice, setPlayerChoice ] = useState(null);
const [ puterChoice, setComputerChoice ] = useState(null);
const [ result, setResult ] = useState(null);
useEffect(() => {
setPlayerName(props.location.state.playerName);
setPlayerChoice(props.location.state.playerChoice);
setComputerChoice(generateComputerChoice);
setResult(getResult())
}, []);
const getResult = () => {
// code that runs after the setting of the playerName and playerChoice. Will return "Win", "Lose", or "Draw"
};
const generateComputerChoice = () => {
const outes = [ 'Rock', 'Paper', 'Scissors' ];
return outes[Math.floor(Math.random() * outes.length)];
};
return (
<div className="app-container">
<strong>YOU {result}</strong>
<br />
<strong>{playerName}</strong> chose <strong>{playerChoice}</strong>
<br />
<strong>Computer</strong> chose <strong>{puterChoice}</strong>
</div>
);
};
export default Result;
So here in this example I grab the playerName
and playerChoice
from a previous page, and then add that to my useState on page load.
After that I randomly generate the puterChoice
.
However, after that I want to use the playerChoice
amd puterChoice
that has been added to the state and use that to see if the game is a win, lose or draw.
result
ends up being null
because I assume that by the time the getResult
function is called, the states haven't been set yet.
Do you guys know what should be done in this situation? Seems like this could be a mon thing considering you might wanna grab data from an API and then do something with that data before wanting to render it.
Share Improve this question asked Sep 7, 2019 at 23:14 pyanpyan 8853 gold badges12 silver badges24 bronze badges 2- The reason you're having trouble is that your ponent is doing too much. You are doing the following things: fetching data, generating random values, querying previous states, paring values, and displaying results. All in one ponent! If it were me, I'd have a ponent that took a human choice and a puter choice and displayed the results. I'd have a ponent that simply took player input and called a callback. And I'd have a stateful mon ancestor that handled the fetching and rendering of the children. – Jared Smith Commented Sep 7, 2019 at 23:23
- Thanks all for the responses. In the end I refactored to have the puter generated choice in the previous ponent page, where the player also makes their choice. This makes both the playerChoice and puterChoice be passed in as props together, meaning that that data can be used without worrying about async. I am interested however in how this problem might be solved anyways. For example if we call from an api and then use that api data to be filtered down (lets say sort it alphabetically or something), how would we use hooks to do that after the useEffect api call? – pyan Commented Sep 8, 2019 at 10:34
3 Answers
Reset to default 1That first effect is unneccessary. Just do
const [playerName, setPlayerName] = useState(props.location.state.playerName);
Use the useMemo hook and add the state variables to its dependency array. It will memoize the result for each render cycle so it is only ever puted when playerName
or playerChoice
update.
const getResult = useMemo(() => {
// code that runs after the setting of the playerName and playerChoice. Will return "Win", "Lose", or "Draw"
}, [playerName, playerChoice]);
Oops, I see now you're trying to save this to the result
state variable, so you can either use a second useEffect with the same dependencies instead of the useMemo I suggested, or in your original snippet instead of calling a getResult()
function you change the signature to getResult(name, choice)
and call setResult with the current render cycle values (right from the props).
useEffect(() => {
const { playerName, playerChoice } = props.location.state;
setPlayerName(playerName);
setPlayerChoice(playerChoice);
setComputerChoice(generateComputerChoice);
setResult(getResult(playerName, playerChoice));
}, []);
const getResult = (name, choice) => {
// Will return "Win", "Lose", or "Draw"
};
setState is asynchronous and you'll be able to use the state only in the next render. Unlike in class ponents, hooks doesn't allow a callback after setting the state.
But looking at your ponent and assuming that is what the functionality of that would be, there's no reason for you to add playerName and playerChoice to the state of the ponent. You can use the data from the props itself.
import React, { useState, useEffect } from 'react';
const Result = (props) => {
const {playerName, playerChoice} = props.location.state;
const [ result, setResult ] = useState(null);
const [puterChoice, setComputerChoice] = useState(null);
useEffect(() => {
setComputerChoice(generateComputerChoice());
getResult();
}, []);
const getResult = () => {
// You can get the playerName, playerChoice from the props.
// You can also setResult here.
};
const generateComputerChoice = () => {
const outes = [ 'Rock', 'Paper', 'Scissors' ];
return outes[Math.floor(Math.random() * outes.length)];
};
return (
<div className="app-container">
<strong>YOU {result}</strong>
<br />
<strong>{playerName}</strong> chose <strong>{playerChoice}</strong>
<br />
<strong>Computer</strong> chose <strong>{puterChoice}</strong>
</div>
);
};
export default Result;
Hope this helps.