I have a component that makes an AJAX call to fetch some data. Henceforth, it is processed through for loop and and an array is created as following:
var path = '/clinic/'+d.id;
res.push(
<Tappable className='clinic' onTap={() => router.transitionTo(path, i)}>
<div id='name'>{d.name}</div>
<div>{d.address}</div>
<div>{ms}</div>
<span>Timings: {d.timings.value_formatted} - {d.timings.value2_formatted}</span>
</Tappable>
);
All the data renders correctly except for the link. All the items in the array get linked to ID of last item in the array.
e.g.
If there are 10 items with ID 1-10, ideally items should link to clinic/1, clinic/2 etc. However, all the items are being routed to clinic/10.
Routes are defined as follows:
import './theme';
import { router, route } from 'reapp-kit';
router(require,
route('home', '/',
route('clinics'),
route('Clinic', 'clinic/:id'),
route('search')
)
);
Here is the complete file: https://gist.github.com/fotuzlab/6e5ef4c98db678190d98
Seems to me like a classical "for loop closure problem". Since variables have function scope in Javascript, your path
and i
variable are actually the same variables for each iteration. A closure is created for your onTap
anonymous function handler, so each anonymous function refer to the same variables from their closures, which will have the last values assigned to the variables after the end of the loop.
You can work around this by using an immediately-invoked function expression (IIFE) that binds each unique value to a new variable (the parameter for the IIF) for each iteration in the loop, like this:
onTap={((iPath, j) => () => router.transitionTo(iPath, j))(path, i)}
Or if there's a possibility to use ES6 (if you're using Babel.js or some other transpiler, or only care to support compatible browsers) you can use the new let
keyword to give the variables block scope instead of function scope, like this:
for (let i = 0; i < this.state.clinics.count; i++) {
var ms = '';
d = this.state.clinics.data[i];
if (d.medical_scheme) {
$.map(d.medical_scheme, function(val, i){
ms += val.label + ' ';
});
}
let path = '/clinic/'+d.id;
res.push(
<Tappable className='clinic' onTap={() => router.transitionTo(path, i)}>
<div id='name'>{d.name}</div>
<div>{d.address}</div>
<div>{ms}</div>
<span>Timings: {d.timings.value_formatted} - {d.timings.value2_formatted}</span>
</Tappable>
);
}