Ajay Pillay
Ajay Pillay

Reputation: 162

Synchronous Meteor Call in React Class Component

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!

Upvotes: 3

Views: 139

Answers (1)

Christian Fritz
Christian Fritz

Reputation: 21354

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

Upvotes: 1

Related Questions