how to update integer value imported from a module in react - javascript

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({})
}

Related

useEffect resets the numStudents state after check out

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);
};

How to color the rows and headers of a table with bootstrap

I need to display 3 types of data with different fields in the same table. To do this, I want to have 3 headers with a different color each.
I use bootstrap to make my design and my code is in Javascript with React.
I wrote the following code to do this (I tried to simplify it but it is normally reproducible)
import * as React from "react";
import { useEffect, useState } from "react";
import { nanoid } from "nanoid";
//props object
type IPropsTable={
currentDatas: (DataType1 | DataType2 | DataType3 | undefined;
}
const TableRequest: React.FC<IPropsTable> = ({ currentDatas }) => {
const [existData1, setExistData1] = useState(false);
const [existData2, setExistData2] = useState(false);
const [existData3, setExistData3] = useState(false);
useEffect(()=>{
if (currentDatas) {
currentDatas.map((currentData) => {
if (currentData.type === "data1") {
setExistData1(true);
} else if (currentData.type === "data2") {
setExistData2(true);
} else if (currentData.type === "data3") {
setExistData3(true);
}
})
}
},[currentDatas])
function renderTableHeaderData1() {
let header = ['someField1', 'someField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableHeaderData2() {
let header = ['someOtherField1', 'someOtherField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableHeaderData3() {
let header = ['someOtherOtherField1', 'someOtherOtherField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableData() {
if(currentDatas){
return currentDatas.map((session) => {
if (session.type === "data1") {
return (
<tr key={nanoid()} className="warning">
<td>{session.someField1}</td>
<td>{session.someField2}</td>
</tr>
)
} else if (session.type === "data2") {
return (
<tr key={nanoid()} className="info">
<td>{session.someOtherField1}</td>
<td>{session.someOtherField2}</td>
</tr>
)
} else if (session.type === "data3") {
return (
<tr key={nanoid()} className="success">
<td>{session.someOtherOtherField1}</td>
<td>{session.someOtherOtherField2}</td>
</tr>
)
}
})
} else{return undefined}
}
return (
<>
<div>
<table className="table table-sm">
<caption>Result Search</caption>
<thead>
{existData1?
<tr className="thead-warning">{renderTableHeaderData1()}</tr>
: <></>
}
{existData2?
<tr className="thead-info">{renderTableHeaderData2()}</tr>
: <></>
}
{existData3?
<tr className="thead-success">{renderTableHeaderData3()}</tr>
: <></>
}
</thead>
<tbody>
{renderTableData()}
</tbody>
</table>
</div>
</>
)
}
export default TableRequest;
As you can see in the code above, I assign a css class to each of my <tr> (warning for data1, info for data2 and success for data3). But when my component is rendered, no color appears and the table is completely white, either for each of the three headers or for the data contained in each row of the table.
I tried using the thead-warning, thead-info and thead-success classes for my table header tr css classes, they seemed to be more suitable. But same result, no color is displayed.
Does anyone see what I'm doing wrong and could guide me in the right direction, I really don't understand where my problem is.
Use <tr class="success"> or <tr class="warning"> (depends what color you want).
Example:
<tr class="success">
<td>Success</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
My problem was with the name of the className I was using to color my table. By using bg-success instead of table-success everything works normally. But I don't understand why the table-success class doesn't work as in this example: example with table-success

Exporting state of function from child to parent component

I am a beginner in React+Redux and have this code for a table. The mapping of the table goes as follows:-
ListTemp.js
//editTemp function invoked in the mapping below
editTemp = (e, temp_id, temp_name, action_url, duration) => {
e.preventDefault();
this.props.getTemp(temp_id)
this.setState({
isEdit: true,
temp_id : temp_id
});
}
changePage(){
this.setState({
isEdit : false
});
}
render()
if(this.state.isEdit === true){
return(
<EditTemplate changePage = {this.changePage} isEdit={this.state.isEdit}
email = {this.props.email}/>)
}
else
{
return(
<table className = "table_style">
<thead>
<tr className = "table_style" >
<th className = "table_style">TempID</th>
<th className = "table_style">Name</th>
<th className = "table_style">CreatedOn</th>
<th className = "table_style">Action</th>
</tr>
</thead>
<tbody>
{
this.props.temp_list.map((temp,index)=>
<tr className = "table_style">
<th className = "table_style">{temp.temp_id}</th>
<td className = "table_style">{temp.temp_name}</td>
<td className = "table_style">{temp.created_on}</td>
<td>
<button className="btn btn-info" type = "submit" onClick = {(e) => this.editTemp(e, temp.temp_id, temp.temp_name, temp.action_url, temp.duration)}>
EDIT
</button>
</td>
</tr>
)
}
</tbody>
</table>
Because the table was having a lot of data, I paginated it and the mapping was moved to a different file along with the editTemp function being called in onClick in the code changing it as follows:-
ListTemp.js
showTemp = (temp) => {
var result = null;
if (temp.length > 0) {
result = temp.map((temp, index) => {
return <ListMapp key={index} temp={temp} index={index} />;
});
}
return result;
};
changePage(){
this.setState({
isEdit : false
});
}
render()
if(this.state.isEdit === true){
return(
<EditTemplate changePage = {this.changePage} isEdit={this.state.isEdit}
email = {this.props.email}/>)
}
else
{
return(
<table className = "table table-bordered table-hover">
<thead>
<tr className = "table_style" >
<th className = "table_style">TempID</th>
<th className = "table_style">Name</th>
<th className = "table_style">CreatedOn</th>
<th className = "table_style">Action</th>
</tr>
</thead>
<tbody>
{
this.showTemp(rowsPerPage)
}
</tbody>
</table>
So the mapping in this.showTemp now comes from:-
ListMapp.js
class ListMapp extends Component{
constructor(props)
{
super(props);
this.state ={
isEdit : false,
temp_id : '',
}
this.editTemp = this.editTemp.bind(this);
}
editTemp = (e, temp_id, temp_name, action_url, duration) => {
e.preventDefault();
this.props.getTemp(temp_id)
this.setState({
isEdit: true,
temp_id : temp_id
});
}
render() {
var { temp, index } = this.props;
return (
<tr className = "table_style">
<th className = "table_style">{temp.temp_id}</th>
<td className = "table_style">{temp.temp_name}</td>
<td className = "table_style">{temp.created_on}</td>
<td>
<button className="btn btn-info" type = "submit" onClick = {(e) => this.editTemp(e, temp.temp_id, temp.temp_name, temp.action_url, temp.duration)}>
EDIT
</button>
</td>
</tr>
);
}
}
export default ListMapp;
I now need the state of isEdit from the editTemp function in ListMapp.js in ListTemp.js to trigger the if condition in render. How can I do this? In all the related answers I found, the state is sent by linking the onClick to a handleClick function and doing similar in the parent component but I don't see how that can be done here given that onClick in this case is already calling the isEdit function which in turn is utitilising mappping from this.props.temp_list and has it's own parameters as well. Any help, advice, suggestions much appreciated.
You need to lift your state up to the parent component from the child component. I'm assuming this is where you are appending the child component <ListMapp key={index} temp={temp} index={index} />.
Here, you need to pass an event handling function as a prop to the child.
(for instance) <ListMapp key={index} temp={temp} index={index} handler={some_fn}/>
(handler is the prop you'll send to child and sone_fn will be the function that will receive data from the child)
In the child component, you can call props.handler(data) and send whatever data you want to send it to the parent.

How can I integrate searching functionality on table?

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

Trying to get the row information on clicking a row on table in reactJS

I am building a simple app in ReactJS that works with a JSON array by calling a certain API. I am then populating the results of the array in a table. What I now want is to click on any row in the table and get those values to pass into some other component. I am wondering how to get the row information using onClick.
Here is my code.
class ParentComponent extends Component {
constructor(props){
super(props);
this.state = {data: []};
}
componentDidMount() {
fetch('http://hostname:xxxx/yyyy/zzzz')
.then(function(response) {
return response.json();
})
.then(items=>this.setState({data: items}));
}
fetchAccountDetails () {
}
render(){
var newdata = this.state.data;
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
newdata.map(function(account, index){
return (
<tr key={index} data-item={account} onClick={this.fetchAccountDetails()}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
export default ParentComponent;
Pass the index of the state element and retrieve from the state array. Also it is not required to copy state to another variable before mapping, you can do it with state itself
render(){
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
this.state.data.map((account, index) => {
return (
<tr key={index} data-item={account} onClick={() => this.fetchAccountDetails(index)}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
fetchAccountDetails(index) {
var values = this.state.data[index];
console.log(values);
}
fetch the value using
fetchAccountDetails (event) {
Account:event.target.value
this.getData(Account);
}
getData: function(var account)
this.setState({
state: account
});
Now you have your data set as state and you can use it anywhere you want yo

Categories

Resources