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.
Related
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
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({})
}
i want to add a table row by clicking Add, and remove a table row by clicking the small red div inside the table, while retaining the color change option when table is clicked on.
I've been trying for hours, but i'm still new to ReactJS, maybe someone could give me a hint, how to do this, for example with help of an array, a boolean or a for loop? I can't get the right way yet, would be thankful for your input.
i've been thinking about this kind of logic, but haven't been able to implement it yet..
{Boolean(this.state.rows.length) && (
<div onClick={this.handleRemoveRow}></div>
)}
https://jsfiddle.net/mattighof/0uop13kd/
Do the following:
Maintain a state say list and store all your items
Create onClick handlers for adding and removing items in the table
update the state when you add/remove
iterate and render this.state.list
Make sure to do event.stopPropagation() in the remove handler. this way your colour change functionality still works.
See here the implementation of adding and removing item
Code Snippet:
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
tableColor: true,
list: []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
tableColor: !this.state.tableColor
});
}
addItem() {
this.setState({ list: this.state.list.concat("item") });
}
removeItem(e, index) {
e.stopPropagation();
this.setState({ list: this.state.list.filter((_, i) => index !== i) });
}
render() {
return (
<div className="container">
<button onClick={this.addItem} type="button">
Add
</button>
<table>
{this.state.list.map((item, index) => {
return (
<tr>
<td
className={this.state.tableColor ? "trRed" : "trBlack"}
onClick={this.handleClick}
>
{item}
<div
onClick={e => this.removeItem(e, index)}
className="innerDiv"
/>
</td>
</tr>
);
})}
</table>
</div>
);
}
}
This is one of the ways you can do it:
class Table extends React.Component {
constructor(props){
super(props)
this.state ={
rows:[{id:8,name:'item8',tablecColor:'trBlack'}],
tableColor: true
}
this.handleClick = this.handleClick.bind(this);
this.handleAdd = this.handleAdd.bind(this);
this.renderRows = this.renderRows.bind(this);
}
handleClick(clickedRow){
const {rows} = this.state;
let newRows = rows.map(row => {
if(row.id === clickedRow.id) {
row.tableColor = 'trRed'
return row
}
return row;})
this.setState({rows:newRows})
}
handleAdd() {
const {rows} = this.state;
const count = rows.length;
rows.push({id:count,name:count,tablecColor:'trBlack'})
this.setState({rows:rows})
}
renderRows() {
return this.state.rows.map(row => {
return (<tr>
<td className={row.tableColor}>
<div>{row.name}
<div onClick={() => this.handleClick(row)}
className="innerDiv">
</div>
</div>
</td>
</tr>)
});
}
render(){
return (
<div className="container">
<button type="button">Add</button>
<table>
{this.renderRows()}
</table>
</div>
)
}
}
ReactDOM.render(<Table />, document.querySelector("#app"));
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 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