So I created a app takes attendance of students. but when i try to check out useEffect resets the state of numStudents after update.
the code is :
const Attendance = () => {
// Declare state variables
const [students, setStudents] = useState([]);
const [numStudents, setNumStudents] = useState(0);
// Function to add a student to the attendance list
const addStudent = (rollNumber, name) => {
setStudents(
students.concat({
rollNumber,
name,
checkInTime: new Date().toLocaleString(),
})
);
};
// Function to check a student out
const checkOut = (rollNumber) => {
setStudents(
students.map((student) => {
if (student.rollNumber === rollNumber) {
return { ...student, checkOutTime: new Date().toLocaleString() };
}
return student;
})
);
setNumStudents(students.length-1);
};
// Use effect hook to update the number of students when the 'students' state variable changes
useEffect(() => {
setNumStudents(students.length);
}, [students]);
return (
<div>
{/* Form to input student roll number and name */}
<form
onSubmit={(e) => {
e.preventDefault();
addStudent(e.target.rollNumber.value, e.target.name.value);
e.target.rollNumber.value = "";
e.target.name.value = "";
}}
>
<div className="container">
<h1>Student Attendance</h1>
<label>
Roll Number
<input type="number" name="rollNumber" />
</label>
<br />
<label>
Name
<input type="text" name="name" />
</label>
<br />
<button type="submit">Check In</button>
</div>
</form>
{/* Display number of students present */}
<p>There are currently {numStudents} students in the school.</p>
{/* Table to display list of students and their check in/out times */}
<br />
<table className="fl-table">
<thead>
<tr>
<th>Roll Number</th>
<th>Name</th>
<th>Check In Time</th>
<th>Check Out Time</th>
</tr>
</thead>
<tbody>
{students.map((student) => (
<tr key={student.rollNumber}>
<td>{student.rollNumber}</td>
<td>{student.name}</td>
<td>{student.checkInTime.toString()}</td>
<td>
{student.checkOutTime ? (student.checkOutTime.toString()) : (
<button
key={student.rollNumber} onClick={() => checkOut(student.rollNumber)}>
Check Out
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Attendance;
I tried using setNumStudents(students.length-1) in checkOut function but it updates and then useEffect is called that resets it again . How do i make it not reset ? i am new to react Hooks so plz help!!!
Why do you need an effect here at all? I mean you already updating numStudents in checkout handler.
Ideally useEffect should be used to hook up your app to external library or make a http request(like fetching students from server)
When you checkout student you responding to event fired by user, and should update state variables in event handler.
// Function to add a student to the attendance list
const addStudent = (rollNumber, name) => {
setStudents(
students.concat({
rollNumber,
name,
checkInTime: new Date().toLocaleString(),
})
);
setNumStudents(numStudents + 1)
};
// Function to check a student out
const checkOut = (rollNumber) => {
setStudents(
students.map((student) => {
if (student.rollNumber === rollNumber) {
return { ...student, checkOutTime: new Date().toLocaleString() };
}
return student;
})
);
setNumStudents(numStudents - 1);
};
Related
sorry bug again. im new to reactjs, i try to implement functional component and have problem with push a data inside object. i have 2 input tag and everytime i fill a value inside and switch to another the other show undefined. im not sure what is happening in here. help me explain what happen and how to solve it. please advise , thank you so much. below here i put a picture and my code.
my issue
function App() {
const [heldItems, setHeldItems] = useState({
salesno: '',
plu: '',
price: '',
dateandtime: '',
});
const [edit, setEdit] = useState({});
const [salesItemsTemp, setSalesItemsTemp] = useState([]);
const handlerOnEdit = (heldItemsData) => {
console.log(heldItemsData);
setHeldItems(heldItemsData);
setEdit(heldItemsData);
};
const handlerOnChange = (e, type) => {
setHeldItems({
[type]: e.target.value,
});
};
useEffect(() => console.log(heldItems));
const handlerOnSubmit = (e) => {
e.preventDefault();
const data = {
salesno: uniqid(),
plu: heldItems.plu,
price: heldItems.price,
dateandtime: new Date().toLocaleString(),
};
console.log(data);
};
const handlerRemove = (heldItemsSalesNo) => {
let filteredSalesItemsTemp = salesItemsTemp.filter(
(item) => {
return item.salesno !== heldItemsSalesNo;
},
);
setSalesItemsTemp(filteredSalesItemsTemp);
};
return (
<>
<form onSubmit={handlerOnSubmit} autoComplete="off">
<h1>GoGreen Point Of Sales</h1>
<input
type="text"
placeholder="Input item name"
name="plu"
onChange={(e) => handlerOnChange(e, 'plu')}
value={heldItems.plu}
/>
PLU
<input
type="number"
placeholder="Input item price"
name="price"
onChange={(e) => handlerOnChange(e, 'price')}
value={heldItems.price}
/>
Price
<button type="submit">
{edit.salesno ? 'Save Edit Item' : 'Save Item'}
</button>
<div>
<table>
<caption>Sales</caption>
<thead>
<tr>
<th>SalesNo</th>
<th>PLUName</th>
<th>Price</th>
<th>Date & Time</th>
<th>Void</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
{salesItemsTemp.map((sales) => {
const { salesno, plu, price, dateandtime } =
sales;
return (
<tr key={salesno}>
<td>{salesno}</td>
<td>{plu}</td>
<td>{price}</td>
<td>{dateandtime}</td>
<td>
<button
type="button"
onClick={() =>
handlerRemove(salesno)
}>
X
</button>
</td>
<td>
<button
type="button"
onClick={() =>
handlerOnEdit(sales)
}>
Edit
</button>
</td>
</tr>
);
})}
</tbody>
<tfoot>
<tr>
<td>brought to you by ...</td>
</tr>
</tfoot>
</table>
</div>
</form>
</>
);
You are replacing the complete object. The following may help:
const handlerOnChange = (e, type) => {
setHeldItems((prevValue)=>({...prevValue,
[type]: e.target.value,
}));
};
In your handlerOnChange you're replacing the previous state with a new object which has only one property, so you've lost your previous state. To fix it use handlerOnChange like this:
const handlerOnChange = (e, type) => {
setHeldItems(prevState => ({
...prevState,
[type]: e.target.value,
}));
};
The issue is that inside your handlerOnChnage method you replace the previous state, with new state and thus the previous state is lost!! Yes all those 4 types are lost and you are left with just one!!
You wanna preserve the previous state, sure? Then you can do that too..
Never Do this
You might find some solution like :
const handlerOnChange = (e, type) => {
setHeldItems({
...heldItems,
[type]: e.target.value,
});
};
Since the set state is asynchronous you can't expect the state to be updated just after setHelditems gets executed. Want more detail on it ? Visit
More clean solution
Do we have a proper solution? Yes and here it is : Use a callback function and update the state with the use of that
const handlerOnChange = (e, type) => {
setHeldItems(prevSnapshot=>({...prenSnapshot,
[type]: e.target.value,
}));
};
Helpful link
I am new to React, there are two input fields in the application, one is for ID and another for Name, There are two components I've used, in the parent component I've maintained all the state and form in separate another component. My aim is to check the id which is a input from the user, id should be unique every time, if it's same, an alert should popup and the focus turns to ID input field, and it should do the same until the ID is different from all the objects(state object)
My app.js file is,
import React, { Component } from "react";
import Form from "./Form";
export default class App extends Component {
state = {
names: [
/*
{id: 1,name: "Aashiq"}
*/
],
};
renderTable() {
return this.state.names.map((eachName) => {
const { id, name } = eachName;
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>
<input
type="button"
value="Delete"
onClick={() => this.deleteName(eachName.id)}
/>
</td>
</tr>
);
});
}
deleteName = (id) => {
console.log("ID object", id);
this.state.names &&
this.setState({
names: this.state.names.filter((name) => name.id !== id),
});
};
addName = (newName) => {
this.setState({
names: [newName, ...this.state.names],
});
};
render() {
return (
<>
<Form onSubmit={this.addName} names={this.state.names} />
{/* Table */}
<br />
<table id="details">
<tbody>
<tr>
<th>ID</th>
<th>Names</th>
<th>Operation</th>
</tr>
{/* Render dynamic rows
*/}
{this.renderTable()}
</tbody>
</table>
</>
);
}
}
You can see I try to render the data as table and we can delete the row data also
The form.js file is,
import React, { useState } from "react";
// import { uniqueId } from "lodash";
export default function Form(props) {
const [name, setName] = useState("");
const [id, setId] = useState();
const handleSubmit = (e) => {
e.preventDefault();
handleChangeandValidate();
};
const handleChangeandValidate = () => {
const { onSubmit, names } = props;
console.log("Object keys length", Object.keys(names).length);
if (Object.keys(names).length !== 0) {
names.map((name) => {
if (name.id === id) {
alert("Enter unique id");
setId("");
document.getElementById("ID").focus();
} else {
//if different id
onSubmit({ id: id, name: name });
setName("");
setId("");
}
return null;
});
} else {
onSubmit({ id: id, name: name }); // first time
setName("");
setId("");
}
};
return (
<form onSubmit={handleSubmit} id="myform">
<label style={{ fontSize: "20px", fontWeight: "bold" }}>
Name: {""}
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</label>{" "}
<label style={{ fontSize: "20px", fontWeight: "bold" }}>
ID: {""}
<input
type="number"
onChange={(e) => setId(e.target.value)}
required
value={id}
id="ID"
/>
</label>
{""}
<input type="submit" value="Submit" />
</form>
);
}
You can see I've tried to get the state and onSubmit function from the parent component(app.js) and done some logic like comparing all the ID's, but this logic throws some error, please somebody come up with a good solution.
Thanks in advance!
I have modified your code a bit and here is a working example.
Here is what I did:
I used createRef() to create two references that refer to each input field named nameInputRef and idInputRef.
I added ref={nameInputRef} and ref={idInputRef} so that we can get their values on submit.
On submit, I get the values of the name + id using their refs.
to search for whether the ID exists or not, I used Array.find() which would return undefined if the same id doesn't exist in the list of names coming from the props.
in addName(), I used setState() but in the param I used a function to make sure I get the latest list of names as updating the state is asynchronous. Inside I also used ES6's destructuring feature to make a copy of the current list, push the new name to it and then update the state with the new list of names.
I'm trying a simple interactive table using react, where the table displays a set of details of Books, namely name, price, and stock. The details of the books are stored in a separate JS fine as a module and imported into the component file. Also 'stock' value is saved as a number type in the module. When I try to add or reduce the 'stock' value by one using a button, it doesn't make any changes to the value count.
Below is my code:
import React, { Component } from 'react';
import bookStock from '../Models/books'
class Books extends Component {
state = {
bookList: bookStock
}
totalCopies(){
let sum = 0
this.state.bookList.forEach(book => (
sum = sum + book.stock
))
return sum;
}
addBookCount = (book) => {
this.setState({book.stock = book.stock + 1})
}
reduceBookCount = book => {
this.setState({book.stock = book.stock - 1})
}
render() {
const {length} = this.state.bookList
return (
<React.Fragment>
<table className="table table-borderless">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th>No.of Copies</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.bookList.map(book => (
<tr key = {book.id}>
<td>{book.name}</td>
<td>{book.Price}</td>
<td>{book.stock}</td>
<td><button onClick={() => this.addBookCount(book)} className="btn btn-success btn-sm">ADD</button></td>
<td><button onClick={() => this.reduceBookCount(book)} className="btn btn-danger btn-sm">REDUCE</button></td>
</tr>
))}
</tbody>
</table>
<br/>
<h1>Total No.of Books: {length}</h1>
<h1>Total No.of Copies: {this.totalCopies()}</h1>
</React.Fragment>
);
}
}
export default Books;
How can I increment and decrement the stock value? The totalCopies method is working fine and it displays the total number of copies
Your state has a bookList state that maintains the bookStock.However
In the addBookCount function:
You are trying to update the property book.stock which doesn't exist in your state.
Moreover, this isn't the correct way to update the stock.
First, make a copy of the state in a local variable.
Then, find the book passed as an argument into the function in the local variable (bookList).
Update the stock there.
Now, set the local variable as the new state value.
import React, { Component } from 'react';
import bookStock from '../Models/books'
class Books extends Component {
state = {
bookList: bookStock,
bookcount: 0,
}
totalCopies(){
let sum = 0
this.state.bookList.forEach(book => (
sum = sum + book.stock
))
this.setState({bookcount: sum})
}
addBookCount = (book) => {
this.setState({book.stock = book.stock + 1})
}
reduceBookCount = book => {
this.setState({book.stock = book.stock - 1})
}
render() {
const {length} = this.state.bookList
return (
<React.Fragment>
<table className="table table-borderless">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th>No.of Copies</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.bookList.map(book => (
<tr key = {book.id}>
<td>{book.name}</td>
<td>{book.Price}</td>
<td>{book.stock}</td>
<td><button onClick={() => this.addBookCount(book)} className="btn btn-success btn-sm">ADD</button></td>
<td><button onClick={() => this.reduceBookCount(book)} className="btn btn-danger btn-sm">REDUCE</button></td>
</tr>
))}
</tbody>
</table>
<br/>
<h1>Total No.of Books: {length}</h1>
<h1>Total No.of Copies: {this.state.bookcount}</h1>
</React.Fragment>
);
}
}
export default Books;
Can you try this
I have finally found the way. Thanks everyone for responding me
addBookCount = (book) => {
this.state.bookList.filter(b => b.id === book.id).forEach(bk => bk.stock = bk.stock + 1)
this.setState({})
}
reduceBookCount = book => {
this.state.bookList.filter(b => b.id === book.id).forEach(bk => bk.stock = bk.stock - 1)
this.setState({})
}
At the moment, all the available flights that was received from API are successfully loaded on the page. However, I would like to enable the end user to search specific flight, let's say, by flight number and departure date. How can I integrate this searching functionality in the existing codes?
FlightPage.js
render() {
return (
<>
<h2>Flights</h2>
{this.props.loading ? (
<div>Loading...</div>
) : (
<FlightList flights={this.props.flights} />
)}
</>
);
}
}
As you can see the bellow code, I have used table to present the results.I would like to show only one result or blank table when searching is applied. Can you help me to achieve this?
FlightList.js
const FlightList = ({ flights }) => (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{flights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
);
You could use filter to create a searching functionality like
I would at first add an input where I can insert my filter values
FlightPage.js
handleInput: (event) => {
const { name, value } = event.target
this.setState({ [name]: value })
}
render () {
const { filter } = this.state
return (
<>
<input onChange=(this.handleInput) value={filter} name='filter' />
<FlightList flights={this.props.flights} filterValues={filter} />
</>
)
}
Then I would use my state to filter my Object like
FlightList.js
const FlightList = ({ flights, filterValue }) => {
const filterdFlights = flights.filter(flight => Object.values(flight).includes(filterValue))
return (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{filterdFlights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
)};
You need an input for search and filter flights by value of input. Try this
class FlightPage extends React.Component {
state = {
keyword: '',
}
...
getFlights = () => {
const { keyword } = this.state
const { flights } = this.props
return flights.filter(flight => flight.name.includes(keyword)) // name or something else
}
onInputChange = e => {
this.setState({ keyword: e.target.value })
}
render () {
return (
<>
<input onChange=(this.onInputChange) value={this.state.keyword} />
<FlightList flights={this.getFlights()} />
</>
)
}
}
You can filter your flights array using flights.filter or sort it using flights.sort.
You could try to use jquery datatable. It adds a lot of funcionality to tables easy to implement.
DataTable doc
I have data from local json file that I use to create a table.
In Table class component I have the table contains top 10 movies. The data is being displayed from filteredData state variable and are well displayed after loading the table. Above table I have 2 radio buttons, to choose whether I want to search data based on column title or column genre saved in state variable radioSearch by using function searchHandler. Then I have an input field, when I enter a string in it the result is being saved in searchFieldInput state variable, by using updatedSearch function.
Finally, I have submitHandler function in this component to filter the table based on selected radio button(title/genre of the film), and after that based on entered string in input field. The filtered data I am putting into filteredData variable in order to update the state by using setState. Unfortunately no filtering is being done after hitting submit. In Table component is nested TableRow component which should display the data based on applied filtering. I don't know whether the concept of submitHandler function is wrong, and why is not filtering the data? Can somebody help.
Here is my Table component:
import React, {Component} from 'react';
import TableRow from './TableRow/TableRow';
class Table extends Component {
constructor(props) {
super(props)
this.state = {
filteredData: this.props.data,
searchFieldInput: '',
radioSearch: this.props.radioSearch,
transformed: false
}
}
updatedSearch = (event) => {
this.setState({
searchFieldInput: event.target.value
})
}
searchHandler = (e) => {
this.setState({
radioSearch: e.target.value
})
};
submitHandler = (event) => {
event.preventDefault();
if(this.state.radioSearch === "title") {
let filteredData = this.props.data.filter(column => {
return column.title.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
this.setState({
filteredData: filteredData
});
return this.state.filteredData;
} else if(this.state.radioSearch === "genre"){
let filteredData = this.props.data.filter(column => {
return column.genre.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
this.setState({
filteredData: filteredData
});
return this.state.filteredData;
}
console.log(this.state.radioSearch);
}
render() {
let filteredData = this.props.data.filter(column => {
return column.title.toLowerCase().indexOf(this.state.searchFieldInput.toLowerCase()) !== -1;
});
return(
<React.Fragment>
<div className="container-fluid">
<div className="container">
<form>
{/*Search field*/}
<input
className={"Search" + (this.state.transformed === true ?
' transformed' : '')}
type="text"
placeholder={(this.state.transformed === true ?
'' : 'Type here')}
maxLength="20"
value={this.state.searchFieldInput} required
onChange={this.updatedSearch.bind(this)}
/>
<button type="submit">
Search
</button>
{/*Radio buttons*/}
<label htmlFor="title">
<input type="radio" name="title" id="title" value="title" checked={this.state.radioSearch === "title"}
onChange={this.searchHandler}/>
title
</label>
<label htmlFor="genre">
<input type="radio" name="genre" id="genre" value="genre" checked={this.state.radioSearch === "genre"}
onChange={this.searchHandler}/>
genre
</label>
</form>
</div>
<div className="container">
<table>
<thead>
<tr>
<th>No.</th>
<th>Picture</th>
<th>Release date</th>
<th>Genre</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
{this.state.filteredData.map((row, index) => {
return (
<TableRow
numeration={index + 1}
key={row.id}
row={row}
/>
)
})
}
</tbody>
</table>
</div>
</div>
</React.Fragment>
)
}
}
export default Table;
I think its because you forgot to add the function to the submit button:
<button type="submit" onSubmit={this.submitHandler.bind(this)}>
Search
</button>