Getting an object from a table row in React - reactjs

I'm a beginner working on a simple CRUD, and all I want to do is get the user from the selected row. If you look below:
{/* ANCHOR - TABLE ROWS */}
{admins.map((user, index) => (
<tbody key={index}>
<tr>
<td style={{fontWeight: 'bold'}}>{user._id}</td>
<td>{shortenStr(user.username)}</td>
<td>{shortenStr(user.firstName)}</td>
<td>{shortenStr(user.lastName)}</td>
<td>{shortenStr(user.dob)}</td>
<td>
<div className="button-div">
<button id="updateUser"
className="button-update-admin"
onClick={handleClick && console.log(user.firstName)}>
</button>
</div>
</td>
</tr>
</tbody>
))}
Console.log gives me the first names of every user in the table, such as:
John
Steve
Frank
Ideally I would like to pass the user Data into my handle click function so that I can create a context, perhaps. Any help is appreciated, thanks!

Please change -
onClick={handleClick && console.log(user.firstName)}>
To
onClick={() => {
console.log(user.firstName)
}}
Or if you want to have a separate onClick function with the event data -
function handleClick(event, user) {
console.log(user.firstName);
}
...
onClick={(event) => handleClick(event, user)}

Related

Filtering a table from search word

I have a simple table:
<Table bordered striped>
<thead>
<tr>
<th>Account</th>
<th>Status</th>
<th>Date</th>
<th>Requested By</th>
<th>Approved By</th>
<th></th>
</tr>
</thead>
<tbody>
{accountList?.map((account, index) => (
<tr>
<td>{account.name}</td>
<td>{account.status}</td>
<td>
{account.confirmDate == null
? ""
: moment(account.confirmDate).format("DD-MM-YYYY")}
</td>
<td>{account.requestedBy}</td>
<td>{account.approvedBy}</td>
<td style={{ width: 90 }} className={"text-center"}>
<FontAwesomeIcon
icon={faList}
onClick={() => showAccountModal(account)}
/>{" "}
{/* <FontAwesomeIcon
icon={faCreditCard}
onClick={()=>showBankModal(account)}
/> */}
</td>
</tr>
))}
</tbody>
</Table>
And then i Have a free text search box. I would like to filter all accounts, where name contains the search word. I could fairly easy just have to hooks/variables with the full result in one variable and the filtered in another and then swap between the two. But is there something smarter in REACT? Some kind of filter pipe like Angular? I want to do all filtering client side.
You can use array::filter and filter your state in-line right in the render logic.
({ name }) => !nameFilter || name.includes(nameFilter)
Returns true if the nameFilter is falsey ('', null, undefined, 0) and short-circuit the boolean expression and so will return all elements, otherwise will continue evaluating the expression and process name.includes(nameFilter).
Updated code
accountList?.filter(
({ name }) => !nameFilter || name.includes(nameFilter),
).map((account, index) => (...
If you are looking for an in-box solution use https://react-table.tanstack.com/docs/examples/filtering
But your approach is pretty simple: Store raw data (array) like the source of truth, filtered array store in local state.
Add a handler onChange to input something like this setFilteredData(raw.filter(el => el.name.includes(name)))

unique key prop error even with unique key on table row

I am not sure how to correct this issue. I am using a unique ID but react still isn't happy. I tried to do it on each but then it complains about duplicates.
<table>
<tbody>
{!loading &&
products &&
products.map((product) => (
<>
<tr>
<td key={product._id}>{product.name}</td>
<td>
<span>
<strong>{product.desc}</strong>
</span>
</td>
</tr>
</>
))}
</tbody>
</table>
You must put the key in the upper most component: the Fragment. So instead of <> use <React.Fragment key={...}>
or instead of handling the key by yourself, maybe let React handle it for you. I find it pretty handy:
<table>
<tbody>
{
!loading && products && React.Children.toArray(
products.map((product) => (
<tr>
<td>{product.name}</td>
<td>
<span>
<strong>{product.desc}</strong>
</span>
</td>
</tr>
))
)
}
</tbody>
</table>
so you'll never have to worry about assigning keys anymore!
React.Children.toArray
Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
Official documentation: https://reactjs.org/docs/react-api.html#reactchildrentoarray

In react bootstrap how do I show the contents of an array in an html table

I have an array that I add to, each time a button is pressed. That part appears to work. When I output it to a console, I can see that each one is getting added.
const addToPool = () => {
diePool[poolCount] = (
<die
type={diceType}
number={diceNumber}
adjuster={diceAdjuster}
/>
);
poolCount++;
};
How do I loop through and display that in my html table after my form?
return (
<div className="App">
<header className="App-header">
<Form>
<Button onClick={addToPool} aria-controls="diceRoll" aria-expanded={open} variant="secondary" size="sm">
Add to Pool
</Button>
</ButtonToolbar>
</Form>
<Fade in={open}>
<div id="diceRoll">
The result is: {randNum}
</div>
</Fade>
</header>
<Table responsive>
<caption>Dice Pool</caption>
<thead>
<tr>
<th>Type</th>
<th>Number</th>
<th>Adjuster</th>
</tr>
</thead>
<tbody>
</tbody>
</Table>
</div>
);
}
export default App;
In order for React to update the user interface to reflect changes, it expects you to notify it that a change has been made. You do this my modifying the state of your component. In your addToPool method, you are modifying a local variable called diePool. That's just a member variable of the control object, not the state, and React won't know you've made a change.
Instead, modify the state with something like this:
const addToPool(diceType, diceNumber, diceAdjuster) {
this.setState(prevstate => {
return {
diePool: prevstate.diePool.push(
{ type: diceType, number: diceNumber, adjuster: diceAdjuster }
)
}
});
}
For this to work, your component will have to have an initial state to work with. You create this in the constructor:
constructor(props) {
this.state = { diePool: [] };
}
Now, whenever your code calls addToPool, the state will be updated and through setState, React will know about it and call the render method as needed.
In your render method, you will need to consult the state to place the dice rolls in your table. You can do so by using map over your array of dice rolls:
<Table responsive>
// ...snip...
<tbody>
{this.state.diePool.map((roll, index) => {
<tr key={index}>
<td>{roll.type}</td>
<td>{roll.number}</td>
<td>{roll.adjuster}</td>
</tr>
})}
</tbody>
</Table>
(Please note the use of index here to make sure that each table row has a unique index - this is a requirement for React to be able to update table rows individually when it can.)
What's important here is that the component state is a crucial concept in React. If you modify something and you expect React to react (ha!) to it, you'll have to modify the state through setState (not by modifying this.state directly). That's the only way React will know about it. React will then work its magic and call the render methods of your component and its children, as necessary.
inside your body tag, you can iterate through an array using the map method (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/map)
<tbody>
{dice.map((item,index) => {
return(
<tr key={index}>
<td>{item.key1}</td>
etc...
</tr>
)})}
</tbody>
I figured it out and posting it here in case others have trouble:
declared a counter:
const [poolCount, setPoolCount] = useState(0);
Created my addToPool:
const addToPool = () => {
setDiePool([
...diePool,
{
dp1 : poolCount,
dp2 : diceType,
dp3 : diceNumber,
dp4 : diceAdjuster
}
]);
setPoolCount(poolCount+1);
};
Call my addToPool when I click the button:
<Button onClick={addToPool} aria-controls="diceRoll" aria-expanded={open} variant="secondary" size="sm">
Add to Pool
</Button>
Then create my table and loop:
<Table responsive>
<caption>Dice Pool</caption>
<thead>
<tr>
<th>#</th>
<th>Type</th>
<th>Number</th>
<th>Adjuster</th>
</tr>
</thead>
<tbody>
{diePool.map(die => (<tr key={die.dp1}><td>{die.dp1}</td><td>{die.dp2}</td><td>{die.dp3}</td><td>{die.dp4}</td></tr>))}
</tbody>
</Table>

Can't Pass A Parameter for Datatable in Modal React JS

I have a datatable where it pops up a modal window if you select a row. I would like the modal window to display only a datatable of the selected row with additional details. However, passing in the selected row's ID for the modal window seems to not work and I don't know why? Or I'm not sure what is a better way to display information in a modal window?
**EDIT: I have discovered that if I replace this.state.TicketID with t and have t=0 then it also works so it must be something to do with calling this.state.TicketID within the x[]?
const {id, created} = t
})
return (
<div>
<Modal isOpen={this.state.modal} toggle={this.toggleModal} className={this.props.className}>
<ModalHeader toggle={this.toggleModal}>Selected Item </ModalHeader>
<ModalBody>
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<td>Ticket ID</td>
<td>Created On</td>
</tr>
</thead>
<tbody>
<tr key={this.state.TicketID}>
<td>{this.state.x[this.state.TicketID].id}</td>
<td>{this.state.x[this.state.TicketID].created}</td>
</tr>
</tbody>
</table>
</Datatable>
</ModalBody>
</Modal>
</div>
);
}
TicketID is set in an outer function and is kept in the state object. It is updating as expected. I noticed if I do console.log({this.state.x[0].id}) then it works. However, if I do console.log({this.state.x[this.state.TicketID].id}), it gives an Cannot read property 'id' of undefined. Any help or new suggestions would be appreciated!
actually for the current use case you don't even need a modal level state, so you just make the modal component as pure component(normal js function) and pass the values as props!
// pass these form the parent components
const {isOpend , onToggle, ticket:{id,created} ={}}= props // or from `currentActiveTicket` as described below
// if you mounting the modal element on the same datatable level
// and you only change the state of the parent component
// when a row is clicked you can add the `currentActiveTicket` as an object
// value of what you want to display on the modal(make sense)
})
return (
<div>
<Modal isOpen={isOpend} toggle={onToggle} className={this.props.className}>
<ModalHeader toggle={this.toggleModal}>Selected Item </ModalHeader>
<ModalBody>
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<td>Ticket ID</td>
<td>Created On</td>
</tr>
</thead>
<tbody>
<tr key={id}>
<td>{id}</td>
<td>{created}</td>
</tr>
</tbody>
</table>
</Datatable>
</ModalBody>
</Modal>
</div>
);
}
or you have to maintain the props changing and reflect that on the modal state
for Example I have fallen into this kind of issues because I wasn't understanding these concepts well enough - componentDidUpdate check the references on this

How to filter the data per row? Using a search input method, instantly update the rows based on what user inputs in react.js

I was wondering how would I filter the data i have below with a search input, the data being looped currently is displayed per row. I'm trying to filter it based on user input from a search box. Mainly speaking, what would be ideal way to filter based on the support number, airline name, product, title and status when user inputs a value? Thinking of using array.prototype.filter() Thank you
//Search input box, track what user inputs
onChangeWorkInProgressHLESearchBar(e) {
this.setState({
WorkInProgressSearchTerm: e.target.value
});
console.log(this.state.WorkInProgressSearchTerm);
}
//DATA being looped through per row
const WorkInProgressHLEsData = () => {
return (
<tbody>
<th>SUP#</th>
<th>AIRLINE</th>
<th>PRODUCT</th>
<th>TITLE</th>
<th>STATUS</th>
<th>DATE HLE REQUEST RECEIVED</th>
<th>HLE DEV (DAYS)</th>
<th>HLE CLIENT (DAYS)</th>
<th>DATE HLE SENT</th>
<th>DATE HLE APPROVED / REFUSED</th>
<th>COMMENTS</th>
<th>EDIT ROW</th>
{this.state.WIPHLEsData.map((WIPHLEsData, i) =>
(
<tr key={i}>
<td>
{this.state.WIPHLEsData[i].supNumber}
</td>
<td>
{this.state.WIPHLEsData[i].Airline}
</td>
<td>
{this.state.WIPHLEsData[i].Product}
</td>
<td>
{this.state.WIPHLEsData[i].Title}
</td>
<td>
{this.state.WIPHLEsData[i].Status}
</td>
<td>
{this.state.WIPHLEsData[i].DateHLERequestReceived}
</td>
<td>
{this.state.WIPHLEsData[i].HLEDevDays}
</td>
<td>
{this.state.WIPHLEsData[i].HLEClientDays}
</td>
<td>
{this.state.WIPHLEsData[i].DateHLESent}
</td>
<td>
{this.state.WIPHLEsData[i].DateHLEApprovedRefused}
</td>
<td>
{this.state.WIPHLEsData[i].Comments}
</td>
<td>
{this.state.WIPHLEsData[i].Edit}
</td>
<td>
<Link to={{
pathname: '/WorkInProgressHLEs/supNumber?=' + WIPHLEsData.supNumber,
state: {
supNumber: WIPHLEsData.supNumber,
HLEDevDays: WIPHLEsData.HLEDevDays,
HLEClientDays:WIPHLEsData.HLEClientDays,
DateHLESent: WIPHLEsData.DateHLESent,
DateHLEApprovedRefused: WIPHLEsData.DateHLEApprovedRefused,
Comments: WIPHLEsData.Comments,
Key: this.state.key,
Title: WIPHLEsData.Title
}
}}>
<ButtonToolbar>
<Button bsSize="xsmall" bsStyle="primary"> EDIT</Button>
</ButtonToolbar>
</Link>
</td>
</tr>
))}
</tbody>
);
}
render()
{
<table>
<FormGroup>
<FormControl onBlur={this.onChangeWorkInProgressHLESearchBar.bind(this)} onKeyUp={this.WorkInProgressHLEkeyPress} className="WorkInProgressSearchBar" type="text" placeholder="Search..." />
</FormGroup>{' '}
{WorkInProgressHLEsData()}
</table>
}
I'd really recommend to read the documentation on Array.prototype.filter() which can be used to:
creates a new array with all elements that pass the test implemented by the provided function.
At a very basic level, you can execute filter() in combination with String.prototype.indexOf() prior to executing map() to filter results based on the inclusion of the value of WorkInProgressSearchTerm in the respective object string property. It's not entirely clear what your data structure is and what object property ties to each value you are looking for so this is a simplified example of filtering showing checking supNumber, Title, and Comments properties of each object in WIPHLEsData.
const WorkInProgressHLEsData = () => {
const { WorkInProgressSearchTerm, WIPHLEsData } = this.state;
return WIPHLEsData
.filter(item => {
return item.supNumber.indexOf(WorkInProgressSearchTerm) > -1 ||
item.Title.indexOf(WorkInProgressSearchTerm) > -1 ||
item.Comments.indexOf(WorkInProgressSearchTerm) > -1;
})
.map(item => (
<tr key={item.id}>
<td>{item.supNumber}</td>
<td>{item.Title}</td>
// as many other <td> as you'd need
</tr>
));
};
You can as many checks as necessary within filter() as long as the return statement returns a boolean, true for the ones you want included and false for the ones you want excluded.
One note when using Array.prototype.map(), which I'd recommend looking at the documentation for as well. You don't need to reference the objects via a numeric index [i]. The first argument of the map() callback the current value/object, which you can use to directly access properties for rendering.
I've created a example to show this basic functionlity in action.
Instead of indexOf(WorkInProgressSearchTerm) > -1, you could also consider using String.prototype.includes().
An alternative approach would be to create a state property representing the filtered results and updating the value using filter each time the search term value changes:
onChangeWorkInProgressHLESearchBar(e) {
const WorkInProgressSearchTerm = e.target.value;
const filteredWIPHLEsData = this.state.WIPHLEsData
.filter(item => {
return item.supNumber.indexOf(WorkInProgressSearchTerm) > -1 ||
item.Title.indexOf(WorkInProgressSearchTerm) > -1 ||
item.Comments.indexOf(WorkInProgressSearchTerm) > -1;
});
this.setState({
WorkInProgressSearchTerm,
filteredWIPHLEsData
});
}
You'd then render your table rows using the filtered items instead:
const WorkInProgressHLEsData = () => {
const { WorkInProgressSearchTerm, filteredWIPHLEsData } = this.state;
return filteredWIPHLEsData
.map(item => (
<tr key={item.id}>
<td>{item.supNumber}</td>
<td>{item.Title}</td>
// whatever else you need to render
</tr>
));
};
Hopefully that helps!

Resources