Get input data from react.js table loop - reactjs

I have to change some users point, so in loop how can i take that input value. Without hook
Use state! How can i declare many state. In loop ?
{this.state.data.map((user, index) => (
<tr key={user.id}>
<th scope="row" className="text-center">
{index + 1}
</th>
<td id="_lastname">{user.last_name}</td>
<td id="_firstname">{user.first_name}</td>
<td className="text-center">{user.category}</td>
<td className="text-center">
<input
type="text"
placeholder={user.point}
name="point"
/>
</td>
<td className="text-center">
<button
name="change"
value="true"
onClick={(e) => this.updateHandler(e)}
>
<i className="fas fa-check-circle">upd</i>
</button>
<button name="delete" value="true">
<i className="fas fa-times-circle">del</i>
</button>
</td>
</tr>
))}
Please note i just need that input value.

You want a dynamic input handler for you inputs.
You can have an state field for the input that is an object.
this.state = {
userPoints: {}
}
Then onChange handler to handle the dynamic inputs
handleDynamicInput = (id, event) => {
this.setState(prevState => ({
userPoints: [...prevState.userPoints, {[id]: event.target.value}]
}))
}
And your input should have the handler bind the user so you can update the state with the userid and userpoint.
<td className="text-center">
<input
value={this.state.userPoints[user.id] || ''}
onChange={this.handleChange.bind(this, user.id)}
type="text"
placeholder={user.point}
name="point"
/>
</td>
You userPoint state field should look like this
userPoints: {
...,
{userId: userPoint},
...
}

Related

How can I reverse the table order when I click the button?

I'm creating a table by pulling the information from the API. There is a button next to the head of each table. Let's say I click the button next to the name, it should sort by name. If I click it again, it should change the name order. OrderBy takes a string like "Id", "Name" in the API URL and sorts accordingly. Ascending reverses the order when it is true or false. So I created two states. When I click the buttons it doesn't work the first time, I have to click twice. I'm not sure, but it probably has something to do with my calling getProductList right after changing the state. How can I solve this?
Initial states must be true and "Id":
const [ascending, setAscending] = useState(true);
const [orderBy, setOrderBy] = useState("Id");
handleSort function:
const handleSort = (e) => {
setOrderBy(e.target.name);
setAscending(!ascending);
getProductList();
};
API URL:
`api/common/products/getProductsAll?page=1&pageSize=30&orderBy=${orderBy}&ascending=${ascending}&Code=&Name=`
Table:
return (
<div className={styles.container}>
<button onClick={() => console.log(orderBy,ascending)}>xxx</button>
<Table striped bordered hover variant="dark">
<thead>
<tr>
<th>
<span>Ürün Kodu</span>
<button name="Id" onClick={(e) => handleSort(e)}>
o
</button>
</th>
<th>
<span>Ürün Adı</span>
<button name="Name" onClick={(e) => handleSort(e)}>
o
</button>
</th>
<th>
<span>Fiyat</span>
<button name="Price" onClick={(e) => handleSort(e)}>
o
</button>
</th>
<th>
<span>Para Birimi</span>
<button name="CurrencyId" onClick={(e) => handleSort(e)}>
o
</button>
</th>
<th>
<span>Birim Seti</span>
<button name="Unit" onClick={(e) => handleSort(e)}>
o
</button>
</th>
<th>
<span>İşlemler</span>
<button>o</button>
</th>
</tr>
</thead>
<tbody>
{list?.map((v) => {
return (
<tr key={v.Id}>
<th>{v.Code}</th>
<th>{v.Name}</th>
<th>{v.Price}</th>
<th>Adet</th>
<th>TL</th>
<th>-</th>
</tr>
);
})}
</tbody>
</Table>
</div>
);
};

React UI doesn't get updated, why?

I have a component, called Bucket, each bucket can have multiple challenges, it looks like this,
The code I have is like this, (much-simplified version)
class CLL_Bucket extends Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
…
bucketChallengesIdx: 0, // to track the index of challegnes
bucketChallenges: this.props.bucket.bucketChallenges || [],
…
};
…
this.onBucketChallengeAdd = this.onBucketChallengeAdd.bind(this);
this.onBucketChallengeIDChanged = this.onBucketChallengeIDChanged.bind(this);
this.onBucketChallengeWeightChanged = this.onBucketChallengeWeightChanged.bind(this);
}
render() {
const bucketIdx = this.props.idx;
return (
<div style={styles.container}>
<div key={bucketIdx} className="row" style={styles.bucketStyle}>
<div className="col-md-2">
…
</div>
<div key={bucketIdx} className="col-md-4">
<div>
<label>
<span className="text-center">Challenges<span>
</label>
<button type="button" className="btn btn-success btn-sm" onClick={() => this.onBucketChallengeAdd()}>Add Challenge</button>
</div>
<table className="table table-bordered table-striped show">
<thead>
<tr>
<th>Challenge ID</th>
<th>Weight</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{this.state.bucketChallenges.map(this.renderBucketChallenges.bind(this))}
</tbody>
</table>
</div>
…
</div>
</div>
);
}
renderBucketChallenges(bucketIdx, idx){
return (
<tr key={idx}>
<td className="col-xs-3 input-group-sm">
<Select
options={challengeIDOptions}
defaultValue={challengeIDOptions[0]}
onChange={this.onBucketChallengeIDChanged.bind(this, idx)}
value={this.state.bucketChallengesID}
isSearchable={true}
/>
</td>
<td className="col-xs-1 input-group-sm">
<input type="text" className="form-control" value={this.state.bucketChallengesWeight}
onChange={(e) => this.onBucketChallengeWeightChanged(idx, e)} disabled={this.props.readOnly}>
</input>
</td>
<td className="col-xs-1 input-group-sm">
…
</td>
<td className="col-xs-1 input-group-sm">
<input className="btn btn-danger btn-sm" id="deleteButton" type="button" value="Delete" onClick={() => this.onDeleteChallenge.bind(this, bucketIdx, idx)} />
</td>
</tr>
);
}
onBucketChallengeAdd(){
var newChallengeIndex = this.state.bucketChallengesIdx + 1;
console.log("onBucketChallengeAdd(): " + newChallengeIndex);
this.setState({bucketChallengesIdx: newChallengeIndex});
this.props.onAddChallenge(this.props.idx, newChallengeIndex);
}
The problem I’m having is, when I click the “Add Challenge” button, I can see from the React developer tool that a new challenge is being added, even though the UI is not updated. When I go to a different tab of my app and come back, the UI is added (a new row for the added challenge is there). Why? Any suggestion on how to fix this? Thanks!
The bigger question is, am I on the right track to make this happen? or i should extract challenge portion to a different component?

React method doesn't return table data to display

In React I have a ternary operator returning a component if a condition is met:
{ this.state.houseHoldGroup.length > 0 ? (
<Table className="align-items-center table-flush" responsive>
<thead className="thead-light">
<tr>
<th scope="col">First</th>
<th scope="col">Last</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{this.checkHouseholdGroup()}
</tbody>
</Table>
) : null }
Works good.
Inside this component I have a method: this.checkHouseholdGroup()
The expected behavior is for this method to return the table data inside <tbody></tbody>
checkHouseholdGroup = () => {
const householdDetails = this.state.houseHoldGroup;
householdDetails.forEach(el => {
console.log(el.firstHousehold)
return (
<tr>
<th scope="row">{el.firstHousehold}</th>
<td>{el.lastHousehold}</td>
<td>
<Button
color="primary"
href="#pablo"
onClick={e => e.preventDefault()}
size="sm"
onClick={e => this.submitMember(e)}>
Update
</Button>
</td>
<td>
<Button
color="primary"
href="#pablo"
onClick={e => e.preventDefault()}
size="sm"
onClick={e => this.submitMember(e)}>
Delete
</Button>
</td>
</tr>
)
})
}
I can confirm the element has data. I console.log(el.firstHousehold) can see it's not empty. What am I doing wrong? The expected result would be that it would return my with the data in it.
Have you tried mapping instead of using forEach?
checkHouseholdGroup = () => {
const householdDetails = this.state.houseHoldGroup;
return householdDetails.map(el => {
console.log(el.firstHousehold)
return (
<tr>
<th scope="row">{el.firstHousehold}</th>
<td>{el.lastHousehold}</td>
<td>
<Button
color="primary"
href="#pablo"
onClick={e => e.preventDefault()}
size="sm"
onClick={e => this.submitMember(e)}>
Update
</Button>
</td>
<td>
<Button
color="primary"
href="#pablo"
onClick={e => e.preventDefault()}
size="sm"
onClick={e => this.submitMember(e)}>
Delete
</Button>
</td>
</tr>
)
})
}
Replace householdDetails.forEach with return householdDetails.map and you should be good.
forEach is used to create side effects - it does not return anything. The parent component of checkHouseholdGroup waits for a value to be returned, but nothing comes out of the function.
Using return inside a forEach call will make the returned values go "nowhere". That's why you need to use map (ir returns a list with the elements), and then return the array.

React - How to check/uncheck checkboxes in a loop whose values come from redux store

I am trying to build a Notification settings via React and Redux where my against each email address I have few checkboxes say something in the below format.
import React from 'react'
export class NotificationSettings extends React.Component {
constructor(props) {
super(props);
this.getEmailUserRows = this.getEmailUserRows.bind(this)
}
getEmailUserRows() {
let _this = this
let emailRows = this.props.emailRows
let emailTemplate = emailRows.map((row) => {
return (
<tr key={row.email}>
<td height="70">
<span class="txt-limit">
<small>{row.email}</small>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={row.email+"sttng1"}
name={row.email+"sttng1"}
type="checkbox"/>
<label for="sttng1"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={row.email+"sttng2"}
name={row.email+"sttng2"}
type="checkbox"/>
<label for="sttng2"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={row.email+"sttng3"}
name={row.email+"sttng3"}
type="checkbox"/>
<label for="sttng3"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={row.email+"sttng4"}
name={row.email+"sttng4"}
type="checkbox"/>
<label for="sttng4"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={row.email+"sttng5"}
name={row.email+"sttng5"}
type="checkbox"
/>
<label for="sttng5"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="icon-delete"></span>
</td>
</tr>
)
})
return emailTemplate
}
render() {
return (
<div class="accordian-container open">
<div class="webhook-dtl">
<h4>Notification Settings</h4>
<table
width="100%"
border="0"
cellSpacing="0"
cellPadding="0"
class="transaction-detail">
<tbody>
<tr class="tbl-hdng">
<td width="24%" height="52" align="left">Email ID</td>
<td width="19%" class="no-spc">Transactional<br/>Emails</td>
<td width="13%" class="no-spc">Settlement<br/>Emails</td>
<td width="12%" class="no-spc">CRM<br/>Emails</td>
<td width="12%" class="no-spc">Onboarding<br/>Emails</td>
<td width="10%" class="no-spc">Other<br/>Emails</td>
<td width="10%" class="no-spc">Action</td>
</tr>
{this.getEmailUserRows()}
</tbody>
</table><br/>
<br/>
<button class="cmn-btn right">ADD MORE</button>
</div>
</div>
)
}
}
export default NotificationSettings
My JSON that is stored in the redux store:
{
"status": 0,
"rows": 0,
"message": "Merchant Details returned successfully",
"result": [
{
"email": "dashboard2#yopmail.com",
"emailCategoryList": {
"crmEmails": true,
"settlementEmails": true,
"transactionalEmails": true,
"onboardingEmails": true,
"otherEmails": true
},
"isMerchantBusinessEmail": true
},
{
"email": "vinikaty#gmail.com",
"emailCategoryList": {
"crmEmails": true,
"settlementEmails": false,
"transactionalEmails": false,
"onboardingEmails": true,
"otherEmails": true
},
"isMerchantBusinessEmail": false
}
],
"errorCode": null,
"guid": null
}
Pleae help me check/uncheck checkboxes in React not able to figure out what would be the best approach.
EDIT:
Made changes as suggested:
`import React from 'react';
let NotificationSettingRow = (props) => {
let { emailRowData } = props
const toggleCheckbox = () => {
console.log("Yay")
}
return (
<tr key={emailRowData.email}>
<td height="70">
<span class="txt-limit">
<small>{emailRowData.email}</small>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={emailRowData.email+"sttng1"}
name={emailRowData.email+"sttng1"}
{...(emailRowData.emailCategoryList.transactionalEmails ? {checked: true} : {}) }
onChange={toggleCheckbox}
type="checkbox"/>
<label for="sttng1"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={emailRowData.email+"sttng2"}
name={emailRowData.email+"sttng2"}
type="checkbox"/>
<label for="sttng2"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={emailRowData.email+"sttng3"}
name={emailRowData.email+"sttng3"}
type="checkbox"/>
<label for="sttng3"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={emailRowData.email+"sttng4"}
name={emailRowData.email+"sttng4"}
type="checkbox"/>
<label for="sttng4"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="pure-checkbox tog-check">
<input
id={emailRowData.email+"sttng5"}
name={emailRowData.email+"sttng5"}
type="checkbox"
/>
<label for="sttng5"></label>
</span>
</td>
<td class="no-spc" height="70">
<span class="icon-delete"></span>
</td>
</tr>
)
}
export default NotificationSettingRow`
However i am unable to check/uncheck checkboxes. Please help
Look at he last line in input, it basically evaluates if email in current row is in particular category, and if yes it renders checked=true, otherwise renders nothing
<input
id={row.email+"sttng1"}
name={row.email+"sttng1"}
type="checkbox"
{...(row.emailCategoryList.crmEmails ? {checked: true} : {}) }
/>
While you are at it you could define your component that would be used like:
<MyMailCheck row={row} idPrefix="sttng1" rowKey="crmEmails"/>
to avoid typing pretty much the same code for each email category.
Now how to handle checking and unchecking by the user. You need action that will be fired by the onClick for each checkbox. Something along these lines:
const emailCategoryClick = (email, category) => ({
type: 'EMAIL_CATEGORY_CLICK',
email,
category
})
And you need suitable reducer to recognize this action, and update state accordingly.
Lastly, you need to wire your checkboxes to dispatch correct emailCategoryClick actions. To do so, you should connect your component to get access to dispatch method from store. Something like:
export default connect(
(state) => ({}),
(dispatch) => ({catClick: (email, cat) => () => {dispatch(emailCategoryClick(email, cat))} })
)(NotificationSettings)
and in your checkbox you would add onClick handler as in:
<input
id={row.email+"sttng1"}
name={row.email+"sttng1"}
type="checkbox"
{...(row.emailCategoryList.crmEmails ? {checked: true} : {}) }
onCLick={catClick(row.email, "crmEmails")}
/>

How to reference other inputs in the same "row"?

onChange = ev => {
// how to get all inputs in this row?
};
render = () =>
<table className="MenuMenuOptionsWidget">
<thead>
<tr>
<th className="id">ID</th>
<th className="label">Label</th>
<th className="order">Order</th>
<th className="key">Key</th>
<th className="active">Active</th>
</tr>
</thead>
<tbody>
{this.state.options.map((opt,i) => <tr key={i}>
<td className="id">
{opt.id ? [<input type="hidden" name={`options[${i}][id]`} defaultValue={opt.id}/>,opt.id] : '*' }
</td>
<td className="label">
<input type="text" defaultValue={opt.label} name={`options[${i}][label]`} readOnly={!opt.active} onChange={this.onChange} />
</td>
<td className="order">
<input type="text" className="number" defaultValue={opt.order} name={`options[${i}][order]`} readOnly={!opt.active} onChange={this.onChange}/>
</td>
<td className="key">
<input type="text" defaultValue={opt.key} name={`options[${i}][key]`} readOnly={!opt.active} onChange={this.onChange}/>
</td>
<td className="active">
{opt.id ? <input type="checkbox" value="1" defaultChecked={opt.active} name={`options[${i}][active]`} onChange={this.onChange} /> : <a href="#" className="cr-delete-link" onClick={ev => this.onClickDelete(ev,i)}/>}
</td>
</tr>)}
</tbody>
</table>;
I've got a "loop" in my render() function which renders a bunch of inputs. I want to add the same onChange event to each of them. How can I access all the inputs in the row that caused the change event if I do this?
I can access the input that caused the change event via ev.target, but how do I get the others?
I could use jQuery: $(ev.target).closest('tr').find(...) but I'm looking for the idiomatic React way of doing it.
you want to make use of the ref property. by adding a unique ref to either the tr and calling children or a unique ref to each td, you can grab them when you need them.
<tr key={i} ref={`row${i}`}>
then grab the ref using refs
this.refs.row1.children
will grab all the td's within row1
You can use refs as a function. See here.
Try something like this: https://jsbin.com/kixowo/edit?js,console,output
var Component = React.createClass({
getInitialState() {
return {
options: [
'Row 1',
'Row 2'
]
};
},
rows: [],
onChange(row) {
this.rows[row].forEach((input) => {
// The values of all inputs in the row that changed
console.log(input.getDOMNode().value);
});
},
renderRows() {
this.rows = [];
return this.state.options.map((opt,i) => {
// Store and cache a new row and store inputs in the
// array using `refs` below.
this.rows.push([]);
return (<tr key={i}>
<td>{opt}</td>
<td>
<input ref={(input) => this.rows[i].push(input)} onChange={this.onChange.bind(this, i)} />
</td>
<td>
<input ref={(input) => this.rows[i].push(input)} onChange={this.onChange.bind(this, i)} />
</td>
</tr>);
});
},
render() {
return (
<table>
{this.renderRows()}
</table>
);
}
});
This way you can build up a dynamic set of inputs and call your on change event with the row number as an argument.

Resources