React App - render() - map () - mapping with if-else condition - reactjs

I have my ReactApp created and would like to display a table with data from AWS dynamodb.
I successfully got data from dynamodb transferred to ReactApp with json, through map() in render() of ReactApp, I displayed the table as below:
How it looks now
So now i need to make the table like re-distributed as below:
How i want it to display
So I plan to render the table with if-else statement accordingly:
but once i put in if-else clause in the function, it didn't work and nothing displayed. So would like to seek advices from experts in stackoverflow :) many thanks in advance
Here is my code, the part I'm stuck is in the return section in render():
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
};
}
render() {
const { hits, isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<form>
<div className="container border border-secondary rounded center">
<div className="row">
<div className="col-12">
{' '}
<h4>
<b>Class Mapping</b>
</h4>{' '}
</div>
</div>
<div className=".col-xs-12 center text-center">
<Table responsive striped bordered hover>
<tr>
<th>Class 1</th>
<th>Class 2</th>
<th>Class 3</th>
<th>Class 4</th>
<th>Class 5</th>
</tr>
<tbody>
{hits.map((hit) => {
if (hit.class === 'Class 1') {
<tr>
<td>{hit.name}}</td> <td></td> <td></td> <td></td> <td></td>{' '}
</tr>;
} else if (hit.class === 'Class 2') {
<tr>
<td></td>
<td>{hit.name}}</td> <td></td> <td></td> <td></td>{' '}
</tr>;
}
})}
</tbody>
</Table>
</div>
</div>
</form>
);
}
async componentDidMount() {
this.setState({ isLoading: true });
const response = await fetch('https://xxxxxxx');
const body = await response.json();
this.setState({ hits: body, isLoading: false, error: null });
}
}
export default App;

So you want to take an array that looks like this:
const hits = [
{ name: 'Tammy', class: 'Class 1' },
{ name: 'Sarah', class: 'Class 2' },
{ name: 'Roland', class: 'Class 3' },
{ name: 'Moshe', class: 'Class 4' },
{ name: 'Peter', class: 'Class 5' },
];
And transform it into an array of names, arranged into columns by their classname, like this:
const rows = [
['Tammy', 'Sarah', 'Roland', 'Moshe', 'Peter'],
['Helen', 'Eric', 'Fiona', 'Darren', 'Andy'],
];
Thats going to take a bit of work. I don't think that you will be able to accomplish it with if/else statement inside a few map functions.
Let's try this. I propose adding a class method to your component called getRows. This method will do the transformation. We start by iterating over your hits and grouping the names by class.
Then we setup a while loop, to push the names into the new rows array. While we still have names, keep building rows. Each row will pick a name according to the column and insert it.
Finally, when we are all out of names, we can return the rows to the render function. Then we map the rows, and map the columns in each rows. We also need unique keys for each mapped element.
getRows()
const getRows = () => {
const columns = ['Class 1', 'Class 2', 'Class 3', 'Class 4', 'Class 5'];
const rows = [];
const groupByColumn = this.state.hits.reduce((acc, next) => {
return { ...acc, [next.class]: [...(acc[next.class] || []), next.name] };
}, {});
const haveNames = () => columns.some((column) => (groupByColumn[column] || []).length > 0);
while (haveNames()) {
const newRow = columns.map((column) => {
return (groupByColumn[column] || []).shift() || '';
});
rows.push(newRow);
}
return rows;
};
The complete component:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
};
}
async componentDidMount() {
this.setState({ isLoading: true });
const response = await fetch('https://xxxxxxx');
const body = await response.json();
this.setState({ hits: body, isLoading: false, error: null });
}
getRows = () => {
const columns = ['Class 1', 'Class 2', 'Class 3', 'Class 4', 'Class 5'];
const rows = [];
const groupByColumn = this.state.hits.reduce((acc, next) => {
return { ...acc, [next.class]: [...(acc[next.class] || []), next.name] };
}, {});
const haveNames = () => columns.some((column) => (groupByColumn[column] || []).length > 0);
while (haveNames()) {
const newRow = columns.map((column) => {
return (groupByColumn[column] || []).shift() || '';
});
rows.push(newRow);
}
return rows;
};
render() {
if (this.state.error) {
return <p>{error.message}</p>;
}
if (this.state.isLoading) {
return <p>Loading ...</p>;
}
return (
<form>
<div className="container border border-secondary rounded center">
<div className="row">
<div className="col-12">
{' '}
<h4>
<b>Class Mapping</b>
</h4>{' '}
</div>
</div>
<div className=".col-xs-12 center text-center">
<Table responsive striped bordered hover>
<tr>
<th>Class 1</th>
<th>Class 2</th>
<th>Class 3</th>
<th>Class 4</th>
<th>Class 5</th>
</tr>
<tbody>
{this.getRows().map((row) => (
<tr key={row.reduce((a, b) => a + b)}>
{row.map((column) => (
<td key={column}>{column}</td>
))}
</tr>
))}
</tbody>
</Table>
</div>
</div>
</form>
);
}
}
export default App;
Example code
const hits = [
{ name: 'Tammy', class: 'Class 1' },
{ name: 'Camen', class: 'Class 2' },
{ name: 'Happy', class: 'Class 3' },
{ name: 'Hello Kitty', class: 'Class 4' },
{ name: 'Hello Mimi', class: 'Class 3' },
];
const getRows = (hits) => {
const columns = ['Class 1', 'Class 2', 'Class 3', 'Class 4', 'Class 5'];
const rows = [];
const groupByColumn = hits.reduce((acc, next) => {
return { ...acc, [next.class]: [...(acc[next.class] || []), next.name] };
}, {});
const haveNames = () => columns.some((column) => (groupByColumn[column] || []).length > 0);
while (haveNames()) {
const newRow = columns.map((column) => {
return (groupByColumn[column] || []).shift() || '';
});
rows.push(newRow);
}
return rows;
};
const rows = getRows(hits);
console.log(rows);

Related

Change the input text file in react

New to React and using a simple table. I'm just testing to change an input text value when I select a button on the same row.
The code below is where I'm stuck. I'm trying to figure out how to change the state value "users" for this row when I click on the button. I'm trying to set the first_name to "Testing".
const [users, setUsers] = React.useState(null);
let usersList =
businessUsersState.data.length > 0 &&
businessUsersState.data.map((item: any, key: number) => {
return (
<tr key={key} data-account={item.account_id}>
<td>
<Form.Control name="first-name" type="input" placeholder="First Name" defaultValue={item.first_name} />
</td>
<td>
<Button variant="primary" type="button" onClick={() => {
debugger;
const row = businessUsersState.data.map((item: any) => ({...item}));
row[key].first_name = 'Testing';
const row1 = usersList[key];
//setUserRow(row);
//setUsers(row);
}}>
</Button>
</td>
</tr>
);
});
setUsers(usersList);
I was reading the following link but I cant seem to get it to work. Any help is appreciated.
Following React docs example of object and array in state
const uniqueId = () => {
// always start with a letter (for DOM friendliness)
let idstr = String.fromCharCode(Math.floor(Math.random() * 25 + 65));
do {
const ascicodeChar = Math.floor(Math.random() * 25 + 65);
idstr += String.fromCharCode(ascicodeChar);
idstr += Math.floor(Math.random() * 99);
} while (idstr.length < 8);
return idstr.toLowerCase();
};
const fakeData = [
{ id: uniqueId(), company: 'abc', contact: 'a#gmail.com', country: 'China' },
{ id: uniqueId(), company: 'def', contact: 'b#gmail.com', country: 'Japan' },
{
id: uniqueId(),
company: 'ghj',
contact: 'c#gmail.com',
country: 'Singapore',
},
{
id: uniqueId(),
company: 'ikl',
contact: 'd#gmail.com',
country: 'Indonesia',
},
{
id: uniqueId(),
company: 'mno',
contact: 'e#gmail.com',
country: 'Thailand',
},
];
export default function App() {
const [data, setData] = React.useState(fakeData);
const handleEdit = (id) => {
setData(
data.map((t) => {
// find item matched given id and mutate that item
if (t.id === id) {
return {
id,
company: `test${id}`,
contact: `test${id}#gmail.com`,
country: `test${id}`,
};
} else {
return t;
}
})
);
};
return (
<div>
<table>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Country</th>
<th>edit</th>
</tr>
{(() => {
if (!data.length) {
return <p>No data available</p>;
}
return data.map((i, index) => {
return (
<tr key={i.id}>
<td>{i.company}</td>
<td>{i.contact}</td>
<td>{i.country}</td>
<td>
{/* pass an id of row to edit fnc */}
<button onClick={() => handleEdit(i.id)}>edit</button>
</td>
</tr>
);
});
})()}
</table>
</div>
);
}
You could try to do the same above example.

react table select rows - programmatically select rows based on input props

I have created a react table with select rows following this example.
I'm trying to modify it so that when the data loads, the corresponding checkbox is either checked or unchecked based on the row data's included value. The value doesn't seem to be recognized and when I check/uncheck a row the onChange console.log event isn't being fired. What am I doing wrong.
Heres my Sandbox Example
DATA
[
{
systemId: 13,
deqId: "25007",
facilityId: 6487,
sourceId: "WS002",
sourceName: "GROVE SPRING",
flowRate: 461,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4742,
sourceId: "WS004",
sourceName: "WELL #1",
flowRate: 1100,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4743,
sourceId: "WS005",
sourceName: "100 W (WELL #2) ",
flowRate: 800,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4744,
sourceId: "WS007",
sourceName: "NORTH (WELL #3) ",
flowRate: 900,
flowUom: "GPM ",
included: true
}
];
INDETERMINATE CHECKBOX
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, checked, name, ...rest }, ref) => {
const defaultRef = React.useRef(checked);
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
resolvedRef.current.checked = checked;
}, [resolvedRef, indeterminate, checked]);
return (
<>
<input
type="checkbox"
ref={resolvedRef}
checked={checked}
name={name}
id={name}
{...rest}
/>
</>
);
}
);
REACT TABLE
function ReactTable({
columns,
data,
handleCheckboxSelection,
handleCheckboxStateChange
}) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
rows,
prepareRow,
selectedFlatRows
} = useTable(
{
columns,
data
},
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: (
{ getToggleAllRowsSelectedProps },
handleCheckboxStateChange
) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox
name={row.original.sourceId}
onChange={(row) => console.log(row.original)} //not firing
checked={row.original.included}
{...row.getToggleRowSelectedProps()}
/>
</div>
)
},
...columns
]);
}
);
// Render the UI for your table
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.slice(0, 10).map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
<tfoot>
{footerGroups.map((group) => (
<tr {...group.getFooterGroupProps()}>
{group.headers.map((column) => (
<td {...column.getFooterProps()}>
<b>{column.render("Footer")}</b>
</td>
))}
</tr>
))}
</tfoot>
</table>
<button onClick={() => handleCheckboxSelection(selectedFlatRows)}>
Save
</button>
</>
);
}
TABLE IMPLEMENTATION
const MyDataTable = ({
data
}) => {
const handleCheckboxSelection = (array) => {
console.log(array.map((d) => d.original));
};
const columns = React.useMemo(
() => [
{
Header: "Source ID",
accessor: "sourceId"
},
{
Header: "Source Name",
accessor: "sourceName"
},
{
Header: "Flow Rate (GPM)",
accessor: (d) => {
return d.flowRate ? numberWithCommas(d.flowRate) : "";
}
}
],
[]
);
return (
<ReactTable
columns={columns}
data={data}
handleCheckboxSelection={handleCheckboxSelection}
/>
);
};
The props you added to IndeterminateCheckbox are being overwritten. row.getToggleRowSelectedProps() returns an object:
{
onChange: function,
checked: Boolean,
title: String,
indeterminate: Boolean,
style: Object
}
which overwrites your properties.
The correct way to do what you want to do would be to use
initialState.selectedRowIds property from the useRowSelect API.
Map your data to their included values, then add that array to the initialState as selectedRowIds. In ReactTable.js:
const selectedRowIds = data.map((d, ind) => d.included)
const {
// etc
} = useTable(
{
columns,
data,
initialState: {selectedRowIds}
},
//etc
...columns
}
);
you have to refresh your Table.
you can use useState.
example : you can add onClick={this.mychange} to your save Button.
mychange = async () => {
this.setState({
List: //the new List Data
})
}
and dont forget the Constructor.
constructor() {
super();
this.state = {
List: anyList,
};
}

Delete single row in sqlite DB with react and express

As the title said, im trying to delete a single user with a click on the button in the Table. But it deletes all users. So i think i have to map the single id´s to the button. But how?
This is my first CRUD App, so im not that experienced yet.
here is my React userTable component:
import React, { Component } from 'react'
const API_ENDPOINT = process.env.REACT_APP_API_ENDPOINT
class Userstable extends Component {
constructor(props) {
super(props)
this.state = {
users: [],
isLoading: false,
isError: false
}
}
async componentDidMount() {
this.setState({ isLoading: true })
const response = await fetch(`${API_ENDPOINT}/api/users`)
if (response.ok) {
const users = await response.json()
this.setState({ users, isLoading: false })
} else {
this.setState({ isError: true, isLoading: false })
}
}
render() {
const { users, isLoading, isError } = this.state
if (isLoading) {
return <div>Loading...</div>
}
if (isError) {
return <div>Error</div>
}
return users.length > 0
? (
<table className="table" id="tblData" >
<thead>
<tr>
<th style={{ borderTopLeftRadius: "4px" }}>ID</th>
<th>Name</th>
<th>email</th>
<th style={{ borderTopRightRadius: "4px" }}></th>
</tr>
</thead>
<tbody>
{this.renderTableRows()}
</tbody>
</table>
) : (
<div>
No users.
</div>
)
}
renderTableHeader = () => {
return Object.keys(this.state.users[0]).map(attr =>
<th key={attr} >
{attr}
</th>)
}
deleteTableRow = () => {
return this.state.users.map(user => {
return (
fetch(`${API_ENDPOINT}/api/users/${user.id}`, {method: 'DELETE'})
)
})
}
renderTableRows = () => {
return this.state.users.map(user => {
return (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.regname}</td>
<td>{user.regemail}</td>
<td className="delButton" onClick={this.deleteTableRow}>✕</td>
</tr>
)
})
}
}
export default Userstable
and here is my express Backend route:
router.delete("/users/:id", (req, res, next) => {
var sql = "DELETE FROM Users WHERE id = ?"
var params = [req.params.id]
db.run (sql, params, (err) => {
if (err) {
res.status(400).json({ "error": res.message })
return;
}
res.status(200)
res.json({ "answer": "success" })
return
});
});
thanks!
You can pass the id in onClick:
<td className="delButton" onClick={() => this.deleteTableRow(user.id)}>✕</td>
and send the request with the id:
deleteTableRow = (id) => {
fetch(`${API_ENDPOINT}/api/users/${id}`, {method: 'DELETE'})
}

TypeError: this.state.webOrders.map is not a function

Trying to display the api data in the pageload load in react. It worked fine if I comment the Api call and display from the testdata. Api is working fine and returning results, I was able to log the json to console. But its throwing the error TypeError: this.state.webOrders.map is not a function I tried searching through the forums and I added the isLoading flag, but didn't work.
Anybody has any suggestion or faced the similar issue please let us know. Pasted the whole component code below. I am very new to react, not sure if I am missing any core concepts and asking a dump question.
thanks,
import React, { Component } from 'react'
import axios from 'axios';
import 'bootstrap/dist/js/bootstrap.bundle.min';
import 'bootstrap/dist/css/bootstrap.min.css';
class WebOrderGrid extends Component {
constructor(props) {
super(props) //since we are extending class Table so we have to use super in order to override Component class constructor
this.state = { //state is by default an object
webOrders: [
{ customerAccount: 1, customerName: 'ABC', totalValue: 21, totalweight: .05, webOrderNum: '315689' },
{ customerAccount: 2, customerName: 'TBD', totalValue: 19, totalweight: .03, webOrderNum: '234569' },
{ customerAccount: 3, customerName: 'HHH', totalValue: 16, totalweight: .08, webOrderNum: '11111' },
{ customerAccount: 4, customerName: 'MMMM', totalValue: 25, totalweight: .04,webOrderNum: '8965638' }
],
isLoading : true
}
}
componentDidMount = () =>
{
console.log('componentDidMount');
this.FetchOrders();
}
FetchOrders () {
axios.get("https://localhost:44301/Orders", {
params:{ isRefresh :false }
}).then(result => {
if (result.status === 200) {
console.log('Success 200');
this.setState({webOrders:JSON.stringify(result.data), isLoading : false });
console.log(JSON.stringify(result.data));
/* this.setState({webOrders:JSON.stringify(result.data),loading:false }, function () {
console.log('state:' + this.state.webOrders);
}); */
} else {
console.log('not 200 response');
}
}).catch(e => {
console.log(e.JSON);
});
}
renderTableHeader() {
let header = Object.keys(this.state.webOrders[0])
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
renderTableData() {
const { isLoading, webOrders } = this.state;
return this.props.map((order, index) => {
const { customerAccount, customerName, totalValue, totalweight, webOrderNum } = order //destructuring
return (
<tr scope="row" key={webOrderNum}>
<td scope="col">{customerAccount}</td>
<td scope="col">{customerName}</td>
<td scope="col">{totalValue}</td>
<td scope="col" >{totalweight}</td>
<td scope="col">{webOrderNum}</td>
</tr>
)
})
}
render() {
const { isLoading, webOrders } = this.state;
return (
<div>
<h1 id='title'>webOrders</h1>
<div className="table-responsive">
{!isLoading ?
(
<table id='SalesOrders' className='table table-striped'>
<tbody>
<tr scope="row">{this.renderTableHeader()}</tr>
{this.renderTableData(webOrders)}
</tbody>
</table>
): (
<p>Loading...</p>
)
}
</div>
</div>
)
}
}
export default WebOrderGrid;
Corrections could be seen here.
JSON.stringify converts your array into string. No need to stringify the result.
On renderTableData, instead of this.props.map use webOrders.map
// JSON.stringify converts your array into string.
this.setState({webOrders: result.data, isLoading : false });
renderTableData() {
const { isLoading, webOrders } = this.state;
// use webOrder here.
return webOrders.map((order, index) => {
const { customerAccount, customerName, totalValue, totalweight, webOrderNum } = order //destructuring
return (
<tr scope="row" key={webOrderNum}>
<td scope="col">{customerAccount}</td>
<td scope="col">{customerName}</td>
<td scope="col">{totalValue}</td>
<td scope="col" >{totalweight}</td>
<td scope="col">{webOrderNum}</td>
</tr>
)
})

Many inputs in a table that are writing at the same time REACT

https://github.com/ShannonManoka/Application
I'm coding an application in React the purpose of it is to show 2 table with in one of them a name and in the other one to have an input for each row that are predicting what you are typing. My only problem is that if I am typing something in one input all my inputs are writing at the same time. What am I doing wrong ?
const INPUT_TIMEOUT = 250;
class OldDataTable extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
predictions: [],
};
this.onChange = this.onChange.bind(this);
}
getPredictions(value) {
return [
'Boston',
'Los Angeles',
'San Diego',
'San Franciso',
'Sacramento',
'New York',
'New Jersie',
'Chicago',
].filter(item => item.toLowerCase().indexOf(value.toLowerCase()) !== -1);
}
onChange(e) {
clearTimeout(this.timeout);
const value = e.target.value;
this.setState({
value
});
if (value.length > 0) {
this.timeout = setTimeout(() => {
const predictions = this.getPredictions(value);
this.setState({
predictions
});
}, INPUT_TIMEOUT);
} else {
this.setState({
predictions: []
});
}
}
render() {
return (
<table class='tableOld'>
<tbody>
<tr>
<th>Old Dataset</th>
</tr>
{OldData.map((DataDetails, index)=>{
return <tr>
<td><input type = "text" value={this.state.value} onChange = {this.onChange}/>
<div>
{
this.state.predictions.map((item, index) => (
<div key={index + item}>{item}</div>
))
}
</div></td>
</tr>
})}
</tbody>
</table>
)
}
}
export default OldDataTable
The issue is that all the inputs are referencing the same single value value in state. If you want all separate inputs you'll need a different state value for each.
One option is to interpolate the index into the name of each and dynamically store the state value. For example:
class OldDataTable extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onChange = this.onChange.bind(this);
}
getPredictions(value) {
return [
"Boston",
"Los Angeles",
"San Diego",
"San Franciso",
"Sacramento",
"New York",
"New Jersie",
"Chicago"
].filter(item => item.toLowerCase().indexOf(value.toLowerCase()) !== -1);
}
onChange = index => e => {
clearTimeout(this.timeout);
const value = e.target.value;
this.setState({
[`value${index}`]: value
});
if (value.length > 0) {
this.timeout = setTimeout(() => {
const predictions = this.getPredictions(value);
this.setState({
[`predictions${index}`]: predictions
});
}, INPUT_TIMEOUT);
} else {
this.setState({
[`predictions${index}`]: []
});
}
};
render() {
return (
<table class="tableOld">
<tbody>
<tr>
<th>Old Dataset</th>
</tr>
{OldData.map((_, index) => {
return (
<tr>
<td>
<input
type="text"
value={this.state[`value${index}`]}
onChange={this.onChange(index)}
/>
{this.state[`predictions${index}`] && (
<div>
{this.state[`predictions${index}`].map((item, index) => (
<div key={index + item}>{item}</div>
))}
</div>
)}
</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
There were also two other issues with the code:
Make sure to use className (not class): <table className="tableOld">
Make sure to add a key to the tr element. I added <tr key={index}> but ideally the OldData has some unique identifier that would be better to use. For more details see: https://reactjs.org/docs/lists-and-keys.html

Resources