Created
December 31, 2014 09:19
-
-
Save graue/299dff653c76a0bd1e5a to your computer and use it in GitHub Desktop.
Notes on Bill Fisher and Jing Chen's Flux talk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://speakerdeck.com/fisherwebdev/fluxchat | |
https://www.youtube.com/watch?v=i__969noyAM | |
Stores: "fat models", contain all data for a particular domain | |
Also application logic for that domain | |
Setup: Register with the dispatcher (a pub/sub system, distributes | |
actions to all the stores registered to listen to it) | |
Only input to store is via the callback it hands to the dispatcher | |
Dispatcher sends action to the store via the store's callback | |
If anything has changed based on that action, the store emits a change | |
event for views to come back and check for new data | |
Public interface: Getters, no setters | |
Why? Lack of setters makes it easier to reason about possible states | |
store can be in. | |
Store gets all actions, may not care about all of them, but has the | |
option to change its data as a result of them. | |
Dispatcher doesn't know anything about the store. Only knows it has a | |
callback it has to call at a specific time. | |
View will pull changed data from the store into itself. | |
View doesn't know much about store, only the pieces it's interested | |
in. Store knows nothing about View. | |
Store in particular is its own universe. | |
Dispatcher's register() method has single argument, the callback. | |
That callback, inside of it, has a large switch statement, so store | |
can decide what to do based on the action type. | |
Store updates itself, no one updates it. | |
Stores are based on node's event emitter to emit the change. | |
Controller-views are the ones listening to user events on the | |
application. Like any other React components, but have special job to | |
listen to change events, and when they hear them, from any store, call | |
the stores they're interested in, put that data in their state, and | |
rerender themselves and their children. | |
eg: | |
function getStateFromStores() { | |
return { | |
messages: MessageStore.getAllForCurrentThread(), | |
thread: ThreadStore.getCurrent() // Which thread is current? | |
}; | |
} | |
var MessageSection = React.createClass({ | |
getInitialState() { | |
return getStateFromStores(); | |
} | |
componentDidMount() { | |
MessageStore.addChangeListener(this._onChange); | |
ThreadStore.addChangeListener(this._onChange); | |
} | |
componentWillUnmount() { | |
MessageStore.removeChangeListener(this._onChange); | |
ThreadStore.removeChangeListener(this._onChange); | |
} | |
_onChange() { | |
this.setState(getStateFromStores()); | |
} | |
... | |
} | |
So wait how does MessageStore know the current thread? Does it call | |
into ThreadStore.getCurrent()? Is that allowed? [answer: yes, see | |
below about stores depending on others] | |
When the view responds to a (native) UI event, it'll want to call an | |
action creator method. Action creators wrap creation of an action and | |
pass it to the dispatcher. | |
Just as views can create these actions, a server (API) can create | |
these actions as well. Actions are the entry point into the system. | |
Initialization code (when app first launches), call into API, user | |
interaction, whatevs. | |
We can also within action creator siphon off data and do a write to | |
the API, exiting the system as well. | |
Single store: Actions -> Dispatcher -> Message Store -> Message View | |
Adding threads? | |
Create a thread store and thread view. | |
Actions | |
| | |
v | |
Dispatcher | |
/ \ | |
v v | |
Message Store Thread Store | |
| /--------/ | | |
v v v | |
Message View Thread View | |
Message View listens to both stores, Thread View just the one. | |
(Except thread view shows most recent message. Isn't that gonna be in | |
the message store? Do you put it in both stores?) | |
(Also this seems inefficient at this point. If a view depends on one | |
tiny piece of a store, it gets called on EVERY change to the store, | |
and EVERYTHING it needs from the store must be re-copied and the DOM | |
tree re-diffed. | |
Example: In Pots, if I have a PlayStatusStore that gets called every | |
second to update the current time in the track. This will also force | |
the playlist to diff and consider re-rendering.) | |
"The thread store is something that the message store needs to know | |
about", "already a dependency". OK, why wasn't there an arrow in the | |
diagram then? :) It should be like this: | |
Actions | |
| | |
v | |
Dispatcher | |
/ \ | |
v v | |
Message Store <---- Thread Store | |
| | | | |
| /--------/ | | |
v v v | |
Message View Thread View | |
Add another store to track unread threads? | |
Because we're tracking whether a message has been read in the message | |
itself, we need to have reference to the messages, and thread store is | |
already dependency of message store. So to avoid circular dependency, | |
third store for unread stuff. | |
All of these stores need to be kept in sync. Dispatcher has a method | |
called waitFor. | |
Stores call this inside their callback to say, "I need to wait for | |
this other store to update first." | |
So the unread thread store needs to wait for both message and thread | |
stores to update before it can update itself, as it depends on data in | |
those stores. | |
waitFor() sequences updates, allows hierarchy of stores. | |
start a new conversation (new message, also new thread) | |
action = { | |
type: NEW_THREAD, | |
to: 'Bill', | |
text: 'Hey Bill', | |
threadID: '5e93696f', // temporary on client, just so | |
messageID: '0272fac4', // we have something to refer to it with | |
} | |
Thread Store adds this to map of threads: | |
'5e93696f': { | |
participants: ['Bill', 'Jing'], | |
messageList: ['0272fac4'], | |
} | |
Message Store adds this to map of messages: | |
'0272fac4': { | |
thread: '5e93696f', | |
author: 'Jing', | |
text: 'hey Bill', | |
} | |
Another new feature: group threads. Add a person to a thread. Action: | |
type: ADD_TO_THREAD, | |
threadID: '5e93696f', | |
newParticipant: 'Chris', | |
name: 'Christopher Chedeau', | |
profilePic: 'http://www...' | |
Thread Store updates map of threads: | |
'5e93696f': { | |
participants: ['Bill', 'Jing', 'Chris'], | |
... rest same as before ... | |
} | |
but we don't want to store participant's profile pic, full name etc on | |
thread store, cuz person can be on multiple threads, and don't want | |
that getting out of sync. So we add a third, Participant Store, | |
keeping track of all people we know about on the client. When it sees | |
add to thread, it adds [or presumably updates?] to its map: | |
'Chris': { | |
name: 'Christopher Chedeau', | |
profilePic: 'http://www...' | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment