I’ve been experimenting around with this for quite a while but I haven’t been able to quite figure this out. I am trying to get a synchronous Meteor method call going in a React class component, as part of a registration form I’m doing up. Basically I continually check if a username is taken every time the username input field changes (onChange) and update a visual indicator. I know this isn't ideal design as once you scale it out, it's costly database calls, so I will change this/throttle it as needed, but that's after I get this basic functionality down.
Here is my Meteor method on the server:
checkUsername({ username }) {
var result = false;
if (username != "") {
if (Accounts.findUserByUsername(username)) {
result = true;
}
}
return result;
},
Here is my function in a React class component:
async checkUsername(username) {
Session.set("usernameValid", true);
var syncCall = await Meteor.call(
"checkUsername",
{
username,
}
);
// Shouldn't the line of code below run only AFTER the call above completes?
if (syncCall === false) Session.set("usernameValid", false);
return Session.get("usernameValid");
}
I understand that this shouldn't need to be async, nor should I need await. Furthermore I know this code is wrong since syncCall will always be undefined. This is where I check for username availability:
if (username === "") {
this.state.username = false;
this.setFeedback("username", "", false);
}
else if (this.checkUsername(username)) {
// Username is valid and available
console.log("Username is available!");
this.state.username = username;
this.setFeedback("username", "", true);
}
else {
// Username is invalid and nonempty
console.log("Username is invalid/unavailable!");
this.state.username = false;
this.setFeedback("username", "Username is invalid/unavailable :-(", false);
}
The issue is that the checkUsername function returns before the Meteor call completes, and hence the state isn't updated in time and I can't check that either.
How do I go about forcing this to be synchronous? I read about Meteor.wrapAsync()
but I couldn't nail how to incorporate it properly (or even if it's the right way to go about it). Any help is appreciated, thank you!
Meteor method calls on the client do not return a Promise, so you can't await
them. You need to provide a callback function:
checkUsername(username) {
Session.set("usernameValid", true);
Meteor.call("checkUsername", {username},
(isTaken) => isTaken && Session.set("usernameValid", false));
}
However with React I wouldn't use session variables. React has it's own support for reactive state variables via useState
and it's much better integrated:
const [usernameValid, setUsernameValid] = useState(false);
...
checkUsername(username) {
Meteor.call("checkUsername", {username}, setUsernameValid);
}