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

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>
))}

Related

How to avoid Maximum update depth exceeded Reactjs

I tried to display some csv content in reactjs application using below code. when i click the button it should call the api and get the response.i need to show the response in table formate and when i submit i need to save the data. Getting response and Saving is working fine for me but my issue is when i click the side menu to for fetch the selected data my state not updating properly to display the selected value data i need to click more than three times. so i tried with useeffect its with dependency its wokring fine . but its showing memory issue. i am new to react i tried many ways its showing maximum exceed error. so how to fix it in below code
const Parameters = ({ selectedtestcaseId, hideTestTab }) => {
const [csvattachment, setcsvattachment] = useState({})
const [slectedattachementId, setslectedattachementId] = useState()
const [viewcsv, setviewcsv] = useState()
const [csvHead, setcsvHead] = useState([])
const [csvformFields, setcsvformFields] = useState([{}])
const [formatData, setformatData] = useState([])
const formatCsv = () => {
console.log(csvHead)
if (viewcsv) {
let splitVlaues = viewcsv?.split('\r\n')
setcsvHead(splitVlaues[0]?.split(','))
let allData = []
for (let i = 1; i < splitVlaues.length - 1; i++) {
let a = splitVlaues[i].split('"');
let filteredValue = a.filter(x => x && x != ',');
allData.push(filteredValue)
}
setformatData(allData)
var newval = formatData.map(val => {
return val.reduce((result, field, index) => {
result[csvHead[index]] = field;
return result;
}, {})
})
setcsvformFields(newval)
}
}
useEffect(() => {
formatCsv()
}, [csvformFields])
return (
<table className="table table-striped table-bordered parameter-display">
<thead>
<tr>
{csvHead.map((val, i) => <td key={i}>{val}</td>)}
</tr>
</thead>
<tbody>
<>
{csvformFields?.map((d, index) => (
<tr>
{Object.keys(d).map(prop => (
<>
<td><input type="text" name={prop} value={d[prop]} onChange={(e) => handleFormChange(index, e)} /></td>
</>
))}
</tr>
))}
</>
</tbody>
</table>
)
}
My in input is
in variable "viewcsv" ie const [viewcsv, setviewcsv] = useState()
/
criteriass,criteriaCountry,criteriaCountryState
"5G,NewZealand","5G,New Zealand ","5G,New Zealand "
"5G,NewZealand","5G,New Zealand ","5G,New Zealand "
"5G,NewZealand","5G,New Zealand ","5G,New Zealand "
/
2) const [csvHead, setcsvHead] = useState([])
csvHead hold ['criteriass','criteriaCountry']
3) const [formatData, setformatData] = useState([])
formatData and allData hold
[
[
"5G,NewZealand",
"5G,New Zealand "
]
]
4) const [csvformFields, setcsvformFields] = useState([{}])
csvformFields and newval hold
[
{
"criteriass": "5G,NewZealand",
"criteriaCountry": "5G,New Zealand "
}
]
My issue is after i try to use csvformFields in my html its not loading
after use useEffeect with csvformFields as dependency its working. but throwing infinite loop.
How to fix the above. How to make state change reflect immeditaly
I tried to call "csvformFields" using useEffect its thrwoing maximum exceed error and also i tried with "csvHead " varibale still the same.. How can i fix the maxmium exceed error in the above code . where i am making the mistake. which variable i need to add as dependency array in useEffect and why??
below is the error which i am getting
*
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render
*
The issue comes from your useEffect
useEffect(() => {
formatCsv()
}, [csvformFields])
Here you call out a function called formatCsv whenever the value of csvformFields changes. Inside the formatCsv function you call out setcsvformFields that changes the value of csvformFields. That creates an infinite loop:
useEffect calls formatCsv -> formatCsv changes csvformFields value -> useEffect dependency array trigers and calls the formatCsv function -> formatCsv changes csvformFields value -> so on and so on.
The fix is to fix the dependency array or useEffect. The formatCsv only depend on 3 values - viewcsv formatData and csvHead. They what define csvformFields value. So dependency array should be set to those - not the csvformFields.
useEffect(() => {
formatCsv()
}, [viewcsv, formatData, csvHead])
BUT that wont solve the issue as the infinite loop will still persist. Its down to the flaw of formatCsv function. inside you have setState functions for setformatData and setcsvHead states. Why? I got no clue.
Setting a state and then in the function trying to get the newest value? that wont work. Once a function is executed the values of states inside the function cycle will remain the same no matter how much u change em. so just make them into a variable.
Overall you need to refactor the entire flow. The code is a mess - no proper formatting, dosent follow the standard programing namings, etc. + you HEAVILY rely on SOOO many different states its absurd.
tl;dr; the issue is by setting the wrong useEffect dependency, having too many states and a bad formatCsv function design.

How to properly check the current Index of an array in a React map?

I'm trying to increase the value of an array of an specific position. Let's say you have an array:
const [cantidad, setCantidad] = useState([
{cantidadID: 1, value: 1},
{cantidadID: 2, value: 1},
{cantidadID: 3, value: 1}
]);
Now I would like to change the value of ONLY one of those (doesn't matter which one) with a button
const plus = (e) => {
setCantidad(cantidad[e] + 1);
};
const minus = (e) => {
if (cantidad[e] > 0){
setCantidad(cantidad[e] - 1);
} else {
window.alert("Sorry, Zero limit reached");
setCantidad(0);
}
};
e being the index of the array (with some smart coding ofc) being send from a table pretty much like this
{libros.map((l) => (
<tr>
<td>
<button onClick={mas} />
{cantidad}
<button onClick={menos} />
</td>
<td>{l.grado}</td>
<td>
<input
onChange={(event) => {
let checked = event.target.checked;
}}
type="checkbox"
checked=""
>
</input>
{l.descripcion}
</td>
<td>{l.editorial}</td>
<td>${parseFloat(l.precio).toFixed(2) * cantidad}</td>
</tr>
))}
// I know the checkbox is not working. I'm still working through that.
Now in my head it does make sense that while is mapping there should be a variable controlling the Index of the cantidad variable but if I try to make a new variable inside the map it goes crazy and it crashes, (Unless i'm formating it wrong or putting it in the wrong place)
So I got the logic is very simple but I do not know how to apply it it would be something like:
If you have X[] Variable while mapping make a Y variable that controls the ID of the array variable and if you want to change the value of an specific value from X[] then you must send the X[Y] variable to the button const plus and minus and then only change that variable from that specific ID.
In my full code I'm not using 3 values btw the values are equal to the amount of data that is bringing from the map
Any tips, data or information is appreciate it. If you require the actual input with my whole code let me know as well if I do not get it working I'll probably post the code later.
This is the actual code I'm working on and even though the first question got answered I'm still having issues with the next part (Only relevant part)
const [amount, setAmount] = useState([1]);
//I know this bit doesn't make sense (Yet) I'm trying to figure out first the first bit
const plus = (index) => {
setAmount(amount[index] + 1);
};
const menos = (index) => {
if (amount[index] > 0){
setAmount(amount[index] - 1);
}
else {
window.alert("Sorry, Zero limit reached");
setAmount(0);
}
};
{books.map((l, index) => (
<tr >
<td>
<button onClick = {() => plus(index)}/>
{amount[index]}
<button onClick = {() => minus(index)}/>
</td>
<td>{l.grado}</td>
<td >
<input onChange = {(event) => {
let checked = event.target.checked;
}}
type="checkbox" checked = "">
</input>
{l.descripcion}
</td>
<td >{l.editorial}</td>
<td >${parseFloat(l.precio).toFixed(2) * amount[index]}</td>
</tr>
))}
and this is how is printing
I know in Javascript you can use Array(5).fill(2) is there something similar to that? like I would like to do Array(map.length).fill(1) for example so the value always starts with 1 and then all I have to do is to play with the index to change the correct one with plus and minus.
It is the second argument of .map function so:
libros.map((l, idx)
where idx is your i
Here is documentation about it
You are setting an object in an array based on the index value, and not an actual id. Recommend heavily that you incorporate an id into each object, then grab the object by id to manipulate it. Otherwise you could experience bugs and unexpected behaviors as you manipulate the array.
Same concept as not using the index of an array when using map: this is an anti-pattern.
https://reactjs.org/docs/lists-and-keys.html

map function with table is not working in React

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]);

Read public gists from Github and storing values in Reactapp

I am creating a webapp on react where when you search for a user you can view that users public gists, files and people who have forked their projects.
I was previously able to get values from https://api.github.com/users?since=1234 by looping through the values:
<div>
{
users.map((user, i) => {
return <Usercard
key={users[i].id}
//Virtual DOM needs key prop to keep track of cards
username={users[i].login}
avatar={users[i].avatar_url}
profile={users[i].html_url}
/>
})
}
</div>
However when I use https://api.github.com/gists/public I am a bit lost with the function to read the data from these group of arrays such as files, forks_url, owner properties and so on as I am unable to get the values.
I am calling the data like so:
componentDidMount() {
fetch('https://api.github.com/users')
.then(response => response.json())
.then(users => this.setState({users: users}));
}
In gist URL https://api.github.com/gists/public
The user info is under owner object, if thats what you are looking for.
Pseudo code for rendering gists info in a table:
const renderUserInfo = data.map((value, key) => {
return <tr key={key}>
<td>
{value.owner.login}
</td>
<td>
{value.owner.url}
</td>
</tr>
});
Hope this helps. Let me know if you are still having trouble :D

React make table clickable and edit details

How do I make a table row clickable to edit and update details? I'm retrieving these details from pouchdb.
I'm pasting portion of my code below for your evaluation:
this.state = {docs: []}
this.db = this.props.db
componentDidMount () {
this.updateDocs()
this.db.changes({
since: 'now',
live: true
}).on('change', (change) => {
this.updateDocs()
}).on('error', (err) => {
console.error(err)
})
}
updateDocs () {
this.db.allDocs({include_docs: true}).then((res) => {
var docs = res.rows.map((row) => row.doc)
this.setState({docs})
})
}
And the table below:
<div className='table-list'>
<table>
<thead>
<tr>
<th>Registration Type</th>
<th>First Name</th>
<th>Middle Name</th>
</tr>
</thead>
<tbody>
{this.state.docs.map((doc) => <DataRow key={doc._id} doc={doc} {...this.props} />)}
</tbody>
</table>
</div>
class DataRow extends React.Component {
render () {
let {doc} = this.props
return (
<tr>
<td>{doc.RegistrationInfo['registrationtype']}</td>
<td>{doc.RegistrationInfo['firstname']}</td>
<td>{doc.RegistrationInfo['middlename']}</td>
</tr>
)
}
}
I want to be able to click and edit each of the rows.
My first suggestion - do not do this. Editable grids are quite tough components to implement on your own.
Therefore you have some options to choose from:
Use existing frameworks with editable grids: KendoUI, Wijmo, etc. Although they are quite pricely and most of them have quite pure support for react as for now.
There are some standalone grids with editing functionality: ag-grid, react data grid etc. Some of them are free, other paid.
You can develop your own editable grid based on powerfull components like fixed-data-table, react-virtualized, etc. This approach will still will require some coding to be done but will save you a lot of time.
Make your own components as you are trying now.
If you still would like to go with #4 you can do it this way:
4.1. In state store column of the currently edited cell: editingColumn.
4.2. Assign onClick handler on your <td> tags: <td onClick={(ev) => this.onCellClick(ev))}>. In the handler set editingColumn
4.3. In your render replace
<td>{doc.RegistrationInfo['registrationtype']}</td>
with
<td>{this.renderCell('columnName')}</td>.
And renderCell will look something like this:
private renderCell(colName)
{
if(this.state.editingColumn >= 0 && this.state.editingRow >= 0)
{
// Render your editing control here
// Assign to its 'onChange' like event and save changes to this.props.RegistrationInfo[colName];
}
else
{
return this.props.RegistrationInfo[colName];
}
}
This is very rough description but I hope it will help you get on going.

Resources