Reputation: 438
I have an object that I would like to change the values when button is clicked. Whatever I do, I am only able to change the state on second click. First click is always returning the initial values for me. Can somebody guide me on what is the problem here ?
initial state:
const [currencyResult, setCurrencyResult] = useState({
amount: "1",
currencyFrom: "EUR",
currencyTo: "USD",
multipliedAmount: "",
date: ""
});
onclick :
setCurrencyResult({
amount: currencyResult.amount,
currencyFrom: currencyResult.currencyFrom,
currencyTo: currencyResult.currencyTo,
multipliedAmount: currencyRate * currencyResult.amount,
date: Date.now()
})
whole code is:
import React, { useState, useEffect } from 'react';
import { Form, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import ShowResult from './ShowResult';
import "../styles/HomePage.css"
const HomePage = (props) => {
const options = [
{ key: 'EUR', value: 'EUR', text: 'EUR' },
{ key: 'USD', value: 'USD', text: 'USD' },
{ key: 'CHF', value: 'CHF', text: 'CHF' },
];
const baseUrl = `https://api.exchangeratesapi.io/latest?base=`
const [currencyResult, setCurrencyResult] = useState({
amount: "1",
currencyFrom: "EUR",
currencyTo: "USD",
multipliedAmount: "",
date: ""
});
const [currencyDatabase, setCurrencyDatabase] = useState([]);
const [hasError, setHasError] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const { amount, currencyFrom, currencyTo, multipliedAmount, date } = currencyResult;
const [currencyRate, setCurrencyRate] = useState("");
const [clicked, setClicked] = useState(false);
const formValidation = () => {
if (currencyResult.currencyFrom === currencyResult.currencyTo) setErrorMessage("Equal currencies")
if (!currencyResult.amount) setErrorMessage("no number given")
if (currencyResult.amount.charAt[0] === "-") setErrorMessage("no minus numbers")
if (currencyResult.amount === "0") setErrorMessage("can't convert 0")
if (errorMessage) setHasError(true)
}
const calculationHandler = async () => {
formValidation()
if (hasError) {
return
} else {
setClicked(!false)
const fetchData = await fetch(`${baseUrl}${currencyResult.currencyFrom}&symbols=${currencyResult.currencyTo}`);
const response = await fetchData.json();
setCurrencyRate(await Object.values(response.rates)[0])
setCurrencyDatabase([...currencyDatabase, currencyResult])
setCurrencyResult({
amount: currencyResult.amount,
currencyFrom: currencyResult.currencyFrom,
currencyTo: currencyResult.currencyTo,
multipliedAmount: currencyRate * currencyResult.amount,
date: Date.now()
})
}
}
if (currencyDatabase) props.getCalculations(currencyDatabase);
const changeCurrency = () => {
setCurrencyResult({
...currencyResult,
currencyTo: currencyResult.currencyTo
})
setCurrencyResult({
...currencyResult,
currencyFrom: currencyResult.currencyFrom
})
}
return (
<div className="app">
<div className="header">
<h1 className="headline">Convert currencies in real-time.</h1>
</div>
<div className="form-content">
<Form className="box-background">
<Form.Group style={{ margin: "auto" }} >
<Form.Input
required
label='Amount'
placeholder='Amount'
value={currencyResult.amount}
onChange={(e, { value }) => setCurrencyResult({
...currencyResult,
amount: value
})}
type="number"
/>
<Form.Select
required
placeholder="From"
label="From"
value={currencyResult.currencyFrom}
onChange={(e, { value }) => setCurrencyResult({
...currencyResult,
currencyFrom: value
})}
options={options}
/>
<Icon name="exchange" onClick={changeCurrency} size="large" />
<Form.Select
required
placeholder="To"
label="To"
value={currencyResult.currencyTo}
onChange={(e, { value }) => setCurrencyResult({
...currencyResult,
currencyTo: value
})}
options={options}
/>
<Form.Button
className="btn-div"
onClick={calculationHandler}>
Convert
</Form.Button>
</Form.Group>
<Form.Field className="error-msg">
{hasError ? <p>{errorMessage}</p> : null}
</Form.Field>
</Form>
<Link to="/result" className="conversion-history">
<span>View conversion history {">"}</span>
</Link>
</div>
{currencyResult && <ShowResult currencyResult={currencyResult} />}
</div>
);
}
export default HomePage;
Upvotes: 2
Views: 93
Reputation: 1382
Use functional updates.
If the new state is computed using the previous state, you can [and should] pass a function to setState. The function will receive the previous value, and return an updated value.
https://reactjs.org/docs/hooks-reference.html#usestate
const calculationHandler = async () => {
// async stuff
setCurrencyResult(previous => {
return {
amount: previous.amount,
currencyFrom: previous.currencyFrom,
currencyTo: previous.currencyTo,
multipliedAmount: currencyRate * previous.amount,
date: Date.now()
}
})
}
}
Unlike the setState method found in class components, useState does not automatically merge update objects.
Upvotes: 1