Reputation: 101
I'm trying to build a simple expense log app in React and would like to display the latest data every time the expensesLog
object is updated. On clicking the button, the clickHandler
function is called and the object is updated but nothing seems to change on the UI inspite of me mapping the expenseLog
object. Here's my code :
import './App.css';
import { useState } from 'react';
function App() {
var expenseLog = [
{
name: "Expense1",
amount: 1000
},
{
name: "Expense2",
amount: 100
}
];
var [count, setCount] = useState(0);
var [name, setName] = useState("");
const inputChangeHandler = (e) => {
setName(e.target.value);
}
const inputChangeHandler2 = (e) => {
setCount(e.target.value);
}
const clickHandler = () => {
expenseLog = [...expenseLog, {name: name, amount: count}];
document.querySelector('.inputField').value = "";
document.querySelector('.inputField2').value = 0;
}
return (
<div className="App">
<input onChange={inputChangeHandler} className='inputField'/>
<input type={"number"} onChange={inputChangeHandler2} className='inputField2'/>
<button onClick={clickHandler}></button>
{expenseLog.map(expense => <p>{expense.name} {expense.amount}</p>)}
</div>
);
}
export default App;
Upvotes: 4
Views: 19931
Reputation: 63524
expenses
state.count
and name
states, and add a new one input
that deals with both input changes.input
state when it changes.const { useState } = React;
function Example({ data }) {
const inputInit = { name: '', amount: 0 };
const [expenses, setExpenses] = useState(data);
const [input, setInput] = useState(inputInit);
function handleChange(e) {
const { dataset: { type }, value } = e.target;
setInput({ ...input, [type]: value });
}
function handleClick() {
const { name, amount } = input;
setExpenses([ ...expenses, { name, amount } ]);
setInput(inputInit);
}
return (
<div onChange={handleChange}>
Name: <input data-type="name" type="text" value={input.text}/>
Amount: <input data-type="amount" type="number" value={input.number} />
<button onClick={handleClick}>Add entry</button>
<div className="list">
{expenses.map(obj => {
return <p>{obj.name}: {obj.amount}</p>;
})}
</div>
</div>
);
}
const data=[{name:'Expense1',amount:13},{name:'Expense2',amount:100}];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
* { font-size: 0.95em; }
input { display: block; }
button { margin-top: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Upvotes: 0
Reputation: 187
It's not updating UI as expenseLog is just object, and it's immutable when you don't change the reference.
So, you need to use expenseLog as State variable. And update it inside callback function using setExpenseLog for updating the state.
import "./App.css";
import { useState } from "react";
function App() {
const [expenseLog, setExpenseLog] = useState([
{
name: "Expense1",
amount: 1000,
},
{
name: "Expense2",
amount: 100,
},
];)
var [count, setCount] = useState(0);
var [name, setName] = useState("");
const inputChangeHandler = (e) => {
setName(e.target.value);
};
const inputChangeHandler2 = (e) => {
setCount(e.target.value);
};
const clickHandler = () => {
setExpenseLog(prev => [...prev,{ name: name, amount: count } ])
document.querySelector(".inputField").value = "";
document.querySelector(".inputField2").value = 0;
};
return (
<div className="App">
<input onChange={inputChangeHandler} className="inputField" />
<input
type={"number"}
onChange={inputChangeHandler2}
className="inputField2"
/>
<button onClick={clickHandler}></button>
{expenseLog.map((expense) => (
<p>
{expense.name} {expense.amount}
</p>
))}
</div>
);
}
export default App;
Upvotes: 2
Reputation: 74
Your variable expenseLog
is defined in the function body of your react component. That means react will initialize the variable on every render and thus forget all state that you mutated.
As you did with count
and name
, you need to wrap expensesLog
into a React useState
hook in order to maintain state inside your component.
Upvotes: 2