Exercise 5: Solution
Start with view, add a button
// **** components/app.js
var React = require('react');
var ContactsStore = require('../stores/ContactsStore');
var ViewActionCreators = require('../actions/ViewActionCreators');
var App = React.createClass({
getInitialState () {..},
componentDidMount () {..},
componentWillUnmount () {.. },
handleStoreChange () {.. },
// 2. ++++ add the click handler
deleteContact (contact) {
ViewActionCreators.deleteContact(contact);
},
renderContacts () {
return this.state.contacts.map((contact) => {
return <li>
{contact.first} {contact.last}
// 1. ++++ add the button and click handler
<button onClick={this.deleteContact.bind(this, contact)}>
delete
</button>
</li>;
});
},
render () {
if (!this.state.loaded) {..}
return (
<div>
<ul>{this.renderContacts()}</ul>
</div>
);
}
});
module.exports = App;
Then continues with
ViewActionCreators
var { ActionTypes } = require('../Constants');
var AppDispatcher = require('../AppDispatcher');
var ApiUtil = require('../utils/ApiUtil');
var ViewActionCreators = {
loadContacts () {
AppDispatcher.handleViewAction({
type: ActionTypes.LOAD_CONTACTS
});
ApiUtil.loadContacts();
},
// 1. ++++ add deleteContact
deleteContact (contact) {
AppDispatcher.handleViewAction({
type: ActionTypes.CONTACT_DELETED,
contact: contact
});
ApiUtil.deleteContact(contact);
}
};
module.exports = ViewActionCreators;
deleteContact
can be called from multiple places in your app. Then moves to utils/ApiUtil.js
// **** utils/ApiUtil.js
var xhr = require('../lib/xhr');
var { API, ActionTypes } = require('../Constants');
var ServerActionCreators = require('../actions/ServerActionCreators');
var ApiUtils = {
loadContacts () {
xhr.getJSON(`${API}/contacts`, (err, res) => {
ServerActionCreators.loadedContacts(res.contacts);
});
},
// 1. ++++ add deleteContact
deleteContact (contact) {
// send it to thet contact's `id`
xhr.deleteJSON(`${API}/contacts/${contact.id}`, (err, res) => {
// the only way you know this action has finished
// is because there's another action you care about
// that is fired
ServerActionCreators.deletedContact(contact);
});
}
};
module.exports = ApiUtils;
Then moves the ServerActionCreator
var { ActionTypes } = require('../Constants');
var AppDispatcher = require('../AppDispatcher');
var ServerActionCreators = {
loadedContacts (contacts) {
AppDispatcher.handleServerAction({
type: ActionTypes.CONTACTS_LOADED,
contacts: contacts
});
},
// 1. ++++ dispatch the event
deletedContact (contact) {
AppDispatcher.handleServerAction({
type: ActionTypes.CONTACT_DELETED,
contact: contact
});
}
};
module.exports = ServerActionCreators;
Moves to ContactStore.js
var AppDispatcher = require('../AppDispatcher');
var { EventEmitter } = require('events');
var { ActionTypes } = require('../Constants');
var assign = require('react/lib/Object.assign');
var events = new EventEmitter();
var CHANGE_EVENT = 'CHANGE';
var state = {
contacts: [],
loaded: false
};
var setState = (newState) => {
assign(state, newState);
// emits the change event ('CHANGE')
events.emit(CHANGE_EVENT);
};
var ContactsStore = {
addChangeListener (fn) {
events.addListener(CHANGE_EVENT, fn);
},
removeChangeListener (fn) {
events.removeListener(CHANGE_EVENT, fn);
},
getState () {
return state;
}
};
// 0. need to update the dispatch listener here
// the dispatcher will call all of the callbacks that are listed here
ContactsStore.dispatchToken = AppDispatcher.register((payload) => {
// we save all the info passed in { action }
var { action } = payload;
if (action.type === ActionTypes.CONTACTS_LOADED) {
setState({
loaded: true,
contacts: action.contacts
});
}
// 1. check for CONTACT_DELETED event
if (action.type === ActionTypes.CONTACT_DELETED) {
// change the store's internal state
var newContacts = state.contacts.filter((contact) => {
return contact.id !== action.contact.id;
});
// setState will emit a change event (see above)
setState({ contacts: newContacts });
}
});
module.exports = ContactsStore;
in the end, the store will fire a ‘CHANGE’ event, that the view is listening to:
// **** components/App.js
..
var App = React.createClass({
..
// we're listening to changes on ContactsStore
componentDidMount () {
ContactsStore.addChangeListener(this.handleStoreChange);
ViewActionCreators.loadContacts();
},
handleStoreChange () {
this.setState(ContactsStore.getState());
},
deleteContact (contact) {
ViewActionCreators.deleteContact(contact);
},
renderContacts () {
..
},
render () {
..
}
});
module.exports = App;