Hi Spence,
The most important detail which is missing from your code is that the eachPage
function is asynchronous. I’ll share an updated version that addresses this, and then I’ll explain why it’s important.
solution (click to expand)
let userCount = 0;
client.base('data').select({
filterByFormula: '{userID} = '+user.id
}).eachPage(function page(records, fetchNextPage) {
- records.forEach(function(record) {
- userCount += 1;
- console.log("duplicate found");
- })
+ userCount += records.length;
+ fetchNextPage(); // Don't forget to call this function as per the documetation
}, function done(error) {
+ if (error) {
console.log('error');
return;
+ }
+ if (userCount > 0) {
+ message.channel.send("This user's stats are already being tracked in the database!");
+ } else {
+ create();
+ }
});
-if(userCount === 0) {
+function create() {
client.base('data').create(r
{
"fields": {
"userID": user.id,
"gamesPlayed": 0,
"gamesWon": 0,
"gamesPlayedBlue": 0,
"gamesWonRed": 0,
"gamesPlayedRed": 0,
"gamesWonBlue": 0
}
}
], function(err, records) {
if (err) {
console.error(err);
return;
}
records.forEach(function (record) {
console.log(record.getId());
});
});
-} else {
- message.channel.send("This user's stats are already being tracked in the database!");
}
eachPage
is asynchronous. You call it with a function, and the airtable.js library immediately makes a request to Airtable’s servers to retrieve the data.
Once airtable.js has made a request (and before the response returns), the rest of your script continues to execute. That means
if (userCount === 0) {
// etc.
runs before the response has arrived. The value of userCount
will always be zero at this moment because (as fast as it may seem) the response never returns instantly.
Later, when the response arrives, airtable.js invokes the function you provided (the function named page
in your example). At this point, changes to the userCount
variable no longer make a difference because the code which relies on it has already executed.
One solution is to wrap the code which creates a record in a function, and then to only call that function once the response from Airtable’s servers arrives. That’s what’s going on in the modified version I shared above.
Hi Spence,
The most important detail which is missing from your code is that the eachPage
function is asynchronous. I’ll share an updated version that addresses this, and then I’ll explain why it’s important.
solution (click to expand)
let userCount = 0;
client.base('data').select({
filterByFormula: '{userID} = '+user.id
}).eachPage(function page(records, fetchNextPage) {
- records.forEach(function(record) {
- userCount += 1;
- console.log("duplicate found");
- })
+ userCount += records.length;
+ fetchNextPage(); // Don't forget to call this function as per the documetation
}, function done(error) {
+ if (error) {
console.log('error');
return;
+ }
+ if (userCount > 0) {
+ message.channel.send("This user's stats are already being tracked in the database!");
+ } else {
+ create();
+ }
});
-if(userCount === 0) {
+function create() {
client.base('data').create(r
{
"fields": {
"userID": user.id,
"gamesPlayed": 0,
"gamesWon": 0,
"gamesPlayedBlue": 0,
"gamesWonRed": 0,
"gamesPlayedRed": 0,
"gamesWonBlue": 0
}
}
], function(err, records) {
if (err) {
console.error(err);
return;
}
records.forEach(function (record) {
console.log(record.getId());
});
});
-} else {
- message.channel.send("This user's stats are already being tracked in the database!");
}
eachPage
is asynchronous. You call it with a function, and the airtable.js library immediately makes a request to Airtable’s servers to retrieve the data.
Once airtable.js has made a request (and before the response returns), the rest of your script continues to execute. That means
if (userCount === 0) {
// etc.
runs before the response has arrived. The value of userCount
will always be zero at this moment because (as fast as it may seem) the response never returns instantly.
Later, when the response arrives, airtable.js invokes the function you provided (the function named page
in your example). At this point, changes to the userCount
variable no longer make a difference because the code which relies on it has already executed.
One solution is to wrap the code which creates a record in a function, and then to only call that function once the response from Airtable’s servers arrives. That’s what’s going on in the modified version I shared above.
Hi Mike,
I have a similar problem and tried implementing the solution given by you, but I am still facing some issues. So on landing page, I am getting a user email which I am passing to this component. What I need is, when this component loads, it should figure out if the user already exists, then just give back the data for that user in the return. if user does not exist, then it should add that email to airtable and render a different component stating you are added. What my current code does is, for existing user, it gives the result on the page on the first go, but also adds it like a new user. so next time when I try to see info for this user, it shows me no records in return since it has picked up info for the new user which has nothing in it. Is there anyway I can check for no duplicate constraints or something in airtable directly? Secondly, when a new user is added, it’s added succesfully, but I want to render a different component based on the usercount value in the return, I tried doing that but it failed. Do you have any tips how I could do that?
Here is my code:
const addNewReader = async() =>{
base(‘SubscriberPoints’).create( r
{
“fields”: {
“email”: inputemail, }
}
] )
}
const addorFetch = async () =>{
let userCount = 0;
base('SubscriberPoints').select({filterByFormula: `{email} = "${inputemail}"`,
maxRecords:1
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
console.log('Retrieved', record.get('email'));
console.log(records); // show full record JS object
const readers = records
console.log(readers)
setReaders(readers);
})
userCount = userCount + records.length;
fetchNextPage();
}, function done(err) {
if (err) { console.error(err); return; }
})
if (userCount > 0) {
return;
} else {
addNewReader(inputemail);
}
}
useEffect(() => {
addorFetch();
}, >]);
return (
<AnimationRevealPage disabled>
<Container>
<TwoColumn>
<ImageColumn>
<Image css={imageCss} src={imageSrc} imageBorder={imageBorder} imageShadow={imageShadow} imageRounded={imageRounded}/>
{imageDecoratorBlob && <DecoratorBlob css={imageDecoratorBlobCss} />}
</ImageColumn>
<TextColumn textOnLeft={textOnLeft}>
<TextContent>
<Heading>{heading}</Heading>
<Description>{description}</Description>
<Statistics>
{readers
.map((reader) => (
<Reward
reader={reader}
key={reader.id}
/>
))}
</Statistics>
<PrimaryButton onClick={goBack} buttonRounded={buttonRounded} as="a" >
{primaryButtonText}
</PrimaryButton>
</TextContent>
</TextColumn>
</TwoColumn>
</Container>
</AnimationRevealPage>
);
};
What I ideally want is, if user exists, show them the above return with their points. If it doesnt, then add it in the backend and render a different component which shows, Hey, you have been added. and next time when he logs in, based on the points he has, he will be able to see it.
Hi Mike,
I have a similar problem and tried implementing the solution given by you, but I am still facing some issues. So on landing page, I am getting a user email which I am passing to this component. What I need is, when this component loads, it should figure out if the user already exists, then just give back the data for that user in the return. if user does not exist, then it should add that email to airtable and render a different component stating you are added. What my current code does is, for existing user, it gives the result on the page on the first go, but also adds it like a new user. so next time when I try to see info for this user, it shows me no records in return since it has picked up info for the new user which has nothing in it. Is there anyway I can check for no duplicate constraints or something in airtable directly? Secondly, when a new user is added, it’s added succesfully, but I want to render a different component based on the usercount value in the return, I tried doing that but it failed. Do you have any tips how I could do that?
Here is my code:
const addNewReader = async() =>{
base(‘SubscriberPoints’).create( e
{
“fields”: {
“email”: inputemail, }
}
] )
}
const addorFetch = async () =>{
let userCount = 0;
base('SubscriberPoints').select({filterByFormula: `{email} = "${inputemail}"`,
maxRecords:1
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
console.log('Retrieved', record.get('email'));
console.log(records); // show full record JS object
const readers = records
console.log(readers)
setReaders(readers);
})
userCount = userCount + records.length;
fetchNextPage();
}, function done(err) {
if (err) { console.error(err); return; }
})
if (userCount > 0) {
return;
} else {
addNewReader(inputemail);
}
}
useEffect(() => {
addorFetch();
},
]);
return (
<AnimationRevealPage disabled>
<Container>
<TwoColumn>
<ImageColumn>
<Image css={imageCss} src={imageSrc} imageBorder={imageBorder} imageShadow={imageShadow} imageRounded={imageRounded}/>
{imageDecoratorBlob && <DecoratorBlob css={imageDecoratorBlobCss} />}
</ImageColumn>
<TextColumn textOnLeft={textOnLeft}>
<TextContent>
<Heading>{heading}</Heading>
<Description>{description}</Description>
<Statistics>
{readers
.map((reader) => (
<Reward
reader={reader}
key={reader.id}
/>
))}
</Statistics>
<PrimaryButton onClick={goBack} buttonRounded={buttonRounded} as="a" >
{primaryButtonText}
</PrimaryButton>
</TextContent>
</TextColumn>
</TwoColumn>
</Container>
</AnimationRevealPage>
);
};
What I ideally want is, if user exists, show them the above return with their points. If it doesnt, then add it in the backend and render a different component which shows, Hey, you have been added. and next time when he logs in, based on the points he has, he will be able to see it.
here is the return part
return (
<AnimationRevealPage disabled>
<Container>
<TwoColumn>
<ImageColumn>
<Image css={imageCss} src={imageSrc} imageBorder={imageBorder} imageShadow={imageShadow} imageRounded={imageRounded}/>
{imageDecoratorBlob && <DecoratorBlob css={imageDecoratorBlobCss} />}
</ImageColumn>
<TextColumn textOnLeft={textOnLeft}>
<TextContent>
<Heading>{heading}</Heading>
<Description>{description}</Description>
<Statistics>
{readers
.map((reader) => (
<Reward
reader={reader}
key={reader.id}
/>
))}
</Statistics>
<PrimaryButton onClick={goBack} buttonRounded={buttonRounded} as="a" >
{primaryButtonText}
</PrimaryButton>
</TextContent>
</TextColumn>
</TwoColumn>
</Container>
</AnimationRevealPage>
);
};