235
functions to. That way, they will always have access to setState() as we need. Note that
if any of them needs to touch the state object itself, which they obviously would need to
in
at least some cases, they can do so by accessing this.state, since components always
expose their state via that property.
With the please wait popup showing, we can now call the server:
async function getMailboxes() {
const imapWorker: IMAP.Worker = new IMAP.Worker();
const mailboxes: IMAP.IMailbox[] = await imapWorker.listMailboxes();
mailboxes.forEach((inMailbox) => {
baseComponent.state.addMailboxToList(inMailbox);
});
}
We know that getting a list of mailboxes is an IMAP operation from our look at
the server code, and since the IMAP Worker class on the client seeks to mimic that
API that is exposed by the server to the IMAP Worker class there, it makes sense that
we’d be calling IMAP.Worker.listMailboxes() here too. And the code looks almost
identical to the endpoint handler function code on the server as a result. We’ll look at
the client-side
IMAP close a bit later, but I think you’ll find it rather trivial. The bottom
line, though, is that we get back an array of mailboxes, and we then iterate them and
call the addMailboxToList() method on the state object (which we can do because we
have a reference to the BaseLayout component via the baseComponent variable). That
will update the mailboxes array in state, causing React to render the screen to show the
mailboxes on the left.
And addMailboxToList() method is the next state mutator we’ve hit:
addMailboxToList : function(inMailbox: IMAP.IMailbox): void {
const cl: IMAP.IMailbox[] = this.state.mailboxes.slice(0);
cl.push(inMailbox);
this.setState({ mailboxes : cl });
}.bind(inParentComponent)
First, you have always to remember that when you call setState(), you should never
pass references to objects in state. That may sound weird, but it’s
easy to understand
when dealing with arrays, as we are here. Your first inclination would be to directly push
Chapter 9 Delivering the gooDs: MailBag, the Client
236
inMailbox into state.mailboxes and then try to call this.setState({this.state.
mailboxes}). Everyone tries that at first because it seems reasonable! However, it won’t
work because what you pass into setState() replaces what’s in state at the time, and
trying to do that with what’s already there… well, let’s just say React won’t like you very
much!
Instead, we make a copy of the array via slice(0), then push the new mailbox into
that copy, and finally pass that copy to setState(). Now, everything works as expected.
Note that you only have to do this sort of copying/updating/setting when dealing with
objects and collections.
If you’re paying attention so far, you will have noticed that we haven’t actually called
the server to
get the list of mailboxes yet, we’ve only defined a function to do so. That’s
because the function that calls imapWorker.listMailboxes() must be marked async
since we’re await’ing the response. getMailboxes() is marked async, so now we need to
call it:
getMailboxes().then(function() {
async function getContacts() {
const contactsWorker: Contacts.Worker = new Contacts.Worker();
const contacts: Contacts.IContact[] = await contactsWorker.listContacts();
contacts.forEach((inContact) => {
baseComponent.state.addContactToList(inContact);
});
}
getContacts().then(() =>
baseComponent.state.showHidePleaseWait(false));
});
We don’t want to get the list of contacts until the list of mailboxes is done so that
we know that all server calls are done before the please wait popup is hidden, so we
use the then() syntax to chain them. Inside the then() callback, another function
is defined, getContacts() this time, for the same reason: async/await usage. Once
defined, we can call getContacts() and again use the then() syntax so that we can call
showHidePleaseWait(),
passing false this time, to cause React to hide the please wait
popup.
Chapter 9 Delivering the gooDs: MailBag, the Client
237
The addContactToList() state mutator method is used in there, and it’s virtually
identical to addMailboxToList():
addContactToList : function(inContact: Contacts.IContact): void {
const cl = this.state.contacts.slice(0);
cl.push({ _id : inContact._id,
name : inContact.name, email : inContact.email });
this.setState({ contacts : cl });
}.bind(inParentComponent)
In this case, I’ve constructed the object push()’ed into the contacts array explicitly,
not for any particular reason other than to show that you can. If you wanted the client
contact objects to have different fields than the server-supplied objects for some reason,
this is how you can do that translation.
Do'stlaringiz bilan baham: