map function with table is not working in React - reactjs

Here's the code that I want to render table data with map function in the child component:
const serviceFeeTableData = this.props.serviceFeeTableData;
const serviceFeeDataTable = serviceFeeTableData.map(serviceFeeDataRow => {
return (
<tr>
<td>{`${serviceFeeDataRow.periodFrom} - ${serviceFeeDataRow.periodTo}`}</td>
<td>{serviceFeeDataRow.serviceFeeType}</td>
<td>{serviceFeeDataRow.serviceFee}</td>
<td>{serviceFeeDataRow.refundableStatus}</td>
<td>
<Button variant="primary" size="sm">Edit</Button>
<Button variant="danger" size="sm">Remove</Button>
</td>
</tr>
)
})

As you shown here https://jsfiddle.net/EshanRajapakshe/Lvh6q84t/ the problem is that the state of the parent component only contains one element (and also the response seems to contain only one element)
componentWillReceiveProps(nextProps) {
if (nextProps.getServiceFeeData && nextProps.getServiceFeeData.length > 0) {
this.setState({
periodFrom: nextProps.getServiceFeeData.periodFrom,
periodTo: nextProps.getServiceFeeData.periodTo,
serviceFeeType: nextProps.getServiceFeeData.serviceFeeType,
serviceFee: nextProps.getServiceFeeData.serviceFee,
refundableStatus: nextProps.getServiceFeeData.refundableStatus
})
}
}
You should check the URL you are calling to get a list of data (if you are trying to get the list).
Note also that componentWillReceiveProps is deprecated as described here https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops

const object = {0: {name: 'Stepan'}, 1: {name: 'Oleg'}} // You can not map Object keys
const array = Object.keys(object); // create array from object
array.map(key => object[key]);

Related

.filter() function creating loop in delete function - React

I've got a 'list' component, which allows you to add an item to a state array, and then display it from the state afterwards. These list items can then be removed by the user afterwards (or should be able to).
There's four state props in this component:
currentList: content in the input that's to be added to the list array
setCurrentList: what's used to change the content in the input
fullList: the full array list
setFullList: used to add the currentList content to the array, and removed
I'm using .filter() to create a copy of the state array, and then set the state afterwards in this function:
const deleteFromList = (e) => {
console.log("Delete button pressed")
console.log(e)
let fullList = props.fullListState
let setFullList = props.setFullListState
let filteredArray = fullList.filter(item => item)
setFullList(filteredArray)
}
However, every time I execute this function (i.e. when the delete button is pressed), it just creates a loop and the first two console.logs are just repeatedly done.
This is the full return function itself:
<>
<label className="setup-jobs-label">{props.label}</label>
<div className="setup-jobs-input-container">
<input className="setup-jobs-alt-input" type="text" onChange={onChange} value={props.currentListState} />
<button className="setup-jobs-add-button" onClick={addToList}>Add</button>
</div>
{ props.fullListState === [] ? null : props.fullListState.map(x => {
return <div className="setup-jobs-input-container" key={props.fullListState[x]}>
<p className="setup-jobs-input-paragraph">{x}</p>
<button className="setup-jobs-delete-button" onClick={deleteFromList(x)}>Delete</button>
</div>
}) }
</>
The important bit is the bottom conditional render, which checks to see if the state array is empty, and if so, not display anything. If it isn't, then it returns null.
Any advice would be appreciated - not sure what I'm doing wrong in the filter function.
In your onClick handler, you pass the result of the execution of deleteFromList, you should pass a reference to this function instead :
// note the '() =>'
<button className="setup-jobs-delete-button" onClick={() => deleteFromList(x)}>Delete</button>
See https://reactjs.org/docs/handling-events.html for more details about this.
Beside this, your filter logic does not seem right :
// this line only removes falsy values, but not the "e" values
let filteredArray = fullList.filter(item => item)
// you should implement something like this
let filteredArray = fullList.filter(item => [item is not "e"])
// this should work as we work on objects references
let filteredArray = fullList.filter(item => item !== e)

Using a function inside .map() in React , does not work properly

I tried using a function inside the map() function but yet it does not give the output required. I will explain the problem after these code sections.
map function code segment
{bookings.map((booking) =>(
<tr>
<th scope = "row">{number++}</th>
<td>{booking.tourId}</td>
<td>{booking.bookingDate}</td>
<td>{booking.arrivalDate}</td>
<td>{booking.country}</td>
<td>{GuideAssigned(booking.tourId)}</td>
<td><Button color="warning" style = {{padding: "5px 5px 5px 5px" , width : "80px" , marginBottom : "8px"}}
onClick = {()=>{
history.push(`/assign-guide/${booking.username}`);
}}
>Assign Guide</Button>
</td>
</tr>
))}
The problem here is that the function gets called only for one tour ID , which is the first one in the table.The function does not get called for the other Tour ID's.Am I doing something wrong here?
Below is the function called
function GuideAssigned(tid){
axios.get(`http://localhost:8070/assignedGuides/get/${tid}`).then((res)=>{
console.log(res.data.guideId);
setGuide(res.data.guideId);
if (typeof guide == 'undefined'){
return "Not Assigned";
}
}).catch((err)=>{
console.log(err);
})
return guide;
}
This works only for the first tour ID which gets passed as you can see below.(Guide Assigned field is same for all).
You can't call asynchronous code in the render of a React component. The reason they are all the same is likely because you've one guide state, set by setGuide(res.data.guideId);, and it's also very likely it's the last mapped element setting this value since it's the last to do a GET request.
You will want to make these GET requests when the bookings array reference updates. Use an useEffect hook for this.
const [guides, setGuides] = useState({});
useEffect(() => {
bookings.forEach(({ tourId }) => {
axios.get(`http://localhost:8070/assignedGuides/get/${tourId}`)
.then(res => {
setGuides(guides => ({
...guides,
[tourId]: res.data.guideId,
}));
})
});
}, [bookings]);
Use the mapped tourId to access the correct guides guideId value.
{bookings.map((booking) =>(
<tr>
...
<td>{guides[booking.tourId]}</td> // <-- access guide id by tour id
...
</tr>
))}

React, dynamically add text to ref span

I'm trying to render a message to a span tag specific to an item in a list. I've read a lot about React 'refs', but can't figure out how to populate the span with the message after it's been referenced.
So there's a list of items and each item row has their own button which triggers an API with the id associated with that item. Depending on the API response, i want to update the span tag with the response message, but only for that item
When the list is created the items are looped thru and each item includes this
<span ref={'msg' + data.id}></span><Button onClick={() => this.handleResend(data.id)}>Resend Email</Button>
After the API call, I want to reference the specific span and render the correct message inside of it. But I can't figure out how to render to the span at this point of the code. I know this doesn't work, but it's essentially what I am trying to do. Any ideas?
if (response.status === 200) {
this.refs['msg' + id] = "Email sent";
I recommand using state. because string refs legacy (https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs)
const msgs = [
{ id:1, send:false },
{ id:2, send:false },
{ id:3, send:false },
];
this.state = {
msgs
};
return this.state.msgs.map((msg, index) => {
const status = msg.send ? "Email Sent" : "";
<span>{ status }</span><Button onClick={() => this.handleResend(index)}>Resend Email</Button>
});
async handleResend (index) {
const response = await callAPI(...);
if(reponse.status !== 200) return;
const newMsgs = _.cloneDeep(this.state.msgs);
newMsgs[index].send = true;
this.setState({
msgs: newMsgs
})
}
The workaround is set innerText
this.refs['msg' + id].innerText = "Email sent";
But rather than using ref try to use state to update elements inside render.
i was facing with this issue right now and i figured it out this way:
// currentQuestion is a dynamic Object that comes from somewhere and type is a value
const _target = `${currentQuestion.type}_01`
const _val = this[`${_target}`].current.clientHeight // here is the magic
please note that we don't use . after this to call the ref and not using refs to achieve what we want.
i just guessed that this should be an Object that would hold inner variables of the current object. then since ref is inside of that object then we should be able to call it using dynamic values like above...
i can say that it worked automagically!

Reactjs, how to instanciate object from array, and update render

I'm struggling with reactjs for no reason. I'm a little confused about the magic behind and I'm not able to perform a simple operation of adding object / removing object from an array and display it.
I my parent, I have a method which on click append a new element:
appendNewPma(){
var newPma = this.state.pma.slice();
newPma.push(PmaType1);
this.setState({pma:newPma})
}
then my render method is like that:
render() {
return (
<div>
<a className="waves-effect waves-light btn" onClick={this.appendNewPma}>new</a>
{this.state.pma.map((Item, index) => (
<Item
key = {index}
ref = {"pma" + index.toString()}
onDelete = {() => this.onDelete(index)}
title = {index}/>
))}
</div>
);
}
Append work fine, but my array doesn't contain an object to display but rather a magical function that I don't understand.
But when I try to delete an object:
onDelete(idx){
console.log(idx);
var pma = this.state.pma.slice();
pma.splice(idx, 1);
this.setState({pma:pma})
}
When I delete from the array, no matter what index I will remove, it will only remove the last object. I know my code is not ok, but I have no idea how you can render element for an array of object (here my array is list of function constructor).
It will work better if I could get a straight ref to my object. Of course, I tryed to removed from the ReactDom, but was complening I was not updating from the parent...
I just want a simple array push/pop pattern with update.
Thanks for your help
Try below code. hope so it solve your issue.
addToArray = (event) => {
this.state.pma.push({"name ": "xyz"});
this.setState(
this.state
)
}
removeFromArray =(index) => {
var updatedArr = this.state.pma.splice(index, 1);
this.setState({arr : updatedArr})
}

Modifying state in render

I've got a SPA that has two parts, one of which allows you to choose a geographic area, and another which allows you to pick and choose countries. The country picker subscribes to the geographic area updates so that it can only put up countries that overlap your chosen geographic area. The problem happens when the render method on the CountryPanel attempts to remove countries from the selection if they are no longer in the selected geographic area. So if you select some countries in the eastern hemisphere in the CountryPanel, then go to the GeographicPanel and select an area in the western hemisphere, and the CountryPanel attempts to remove the selected countries from the store, I get the following error in the console:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
The problem occurs in a call to this.props.dispatch(removeCountry(c)) in the following code:
connect((store) => {
return {
min_lat_val: store.session.min_latitude_val,
min_lat_ns: store.session.min_latitude_ns,
max_lat_val: store.session.max_latitude_val,
max_lat_ns: store.session.max_latitude_ns,
min_long_val: store.session.min_longitude_val,
min_long_ew: store.session.min_longitude_ew,
max_long_val: store.session.max_longitude_val,
max_long_ew: store.session.max_longitude_ew,
sel_countries: store.session.selected_countries,
countries: store.extents.countries,
}
})
export class CountryPanel extends React.Component {
constructor(props) {
super(props)
}
render() {
let country_keys = Object.keys(this.props.countries).sort()
if (country_keys.length < 1) {
return <div class="panel-body">
<h3>Countries</h3>
<p className="strong">Country data has not loaded yet!</p>
</div>
}
let countries = []
if (this.props.min_lat_val && this.props.max_lat_val &&
this.props.min_long_val && this.props.max_long_val) {
let tuple = []
country_keys.forEach((c, i) => {
const country = this.props.countries[c]
if (extentsOverlap(
this.props.min_lat_val,
this.props.min_lat_ns,
this.props.max_lat_val,
this.props.max_lat_ns,
this.props.min_long_val,
this.props.min_long_ew,
this.props.max_long_val,
this.props.max_long_ew,
country)) {
tuple.push(<CountryCheck key={c} country={country} />)
if (tuple.length > 2) {
countries.push(tuple)
tuple = []
}
} else {
if (this.props.sel_countries.has(c)) {
this.props.dispatch(removeCountry(c))
}
}
})
if (tuple.length > 0) {
countries.push(tuple)
}
} else {
let tuple = []
country_keys.forEach((c, i) => {
tuple.push(<CountryCheck key={c} country={this.props.countries[c]} />)
if (tuple.length > 2) {
countries.push(tuple)
tuple = []
}
})
if (tuple.length > 0) {
countries.push(tuple)
}
}
const trs = countries.map((c, i) => <tr key={i}>{c}</tr>)
return <div class="panel-body">
<h3>Countries</h3>
<p>If you leave all the checkboxes unchecked, it will return waypoints in all countries that meet all the other criteria. If you select US or Canada, you will be able to select states or provinces from a further list below, or leave those checkboxes empty to select the whole country.</p>
<p>The two letter country codes you see below are from FIPS 10.4, not the more common ISO 3166, which is why you might not recognize some of these codes.</p>
<p><strong>Note: Non-USA data is not as current as USA data, so carefully check this data.</strong> Actually, you should always check all this data against current official data sources.</p>
<table class="table table-striped table-bordered">
<tbody>
{trs}
</tbody>
</table>
</div>
}
}
If I try what's referred to as a "workaround and proof of concept" in https://stackoverflow.com/a/43438560/3333 and change the offending line to setTimeout(() => this.props.dispatch(removeCountry(c)), 0) the warning goes away. Is that the best solution?

Resources