Last active
January 28, 2016 07:18
-
-
Save nathan-muir/415f0167a091f80b1fda to your computer and use it in GitHub Desktop.
Meteor - Connect
This file contains 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
connect = function connect(observeable, ...selectors) { | |
return function (ChildComponent) { | |
const childDisplayName = | |
ChildComponent.displayName || | |
ChildComponent.name || | |
'ChildComponent'; | |
return React.createClass({ | |
displayName: `ConnectedObserver(${childDisplayName})`, | |
componentWillUnmount(){ | |
if (this.handle) { | |
this.handle.stop(); | |
this.handle = null; | |
} | |
this.lastArgs = null; | |
}, | |
installComputation(args, requestChange){ | |
if (this.handle) { | |
this.handle.stop(); | |
} | |
this.handle = observeable(...args.concat(requestChange)); | |
this.lastArgs = args; | |
}, | |
getInitialState() { | |
// special case, we need to get the first result synchronously | |
const args = selectors.map((selector)=>selector(this.props)); | |
var init = true, result = null; | |
const requestChange = (_result)=>{ | |
if (init){ | |
result = _result; | |
} else { | |
this.handleChange(_result); | |
} | |
}; | |
try { | |
this.installComputation(args, requestChange); | |
} finally{ | |
init = false; | |
} | |
return result; | |
}, | |
handleChange(result){ | |
if (this.isMounted()) { | |
this.replaceState(result); | |
} | |
}, | |
componentWillReceiveProps(nextProps){ | |
const args = selectors.map((selector)=> selector(nextProps)); | |
const changed = this.lastArgs.some((value, idx)=> args[idx] !== value); | |
if (changed) { | |
this.installComputation(args, this.handleChange); | |
} | |
}, | |
render(){ | |
return <ChildComponent {...this.props} {...this.state} /> | |
} | |
}) | |
}; | |
}; |
This file contains 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
connectWithTracker = function connectWithTracker(fn, ...selectors){ | |
function wrappedFn(...args){ | |
const onData = args.pop(); | |
return Tracker.autorun(function(){ | |
onData(fn(...args)); | |
}); | |
} | |
return connect(wrappedFn,...selectors); | |
}; | |
connectWithDescriptor = function connectWithDescriptor(descriptor, ...selectors){ | |
function wrappedFn(...args){ | |
const onData = args.pop(); | |
const results = {}; | |
const handles = {}; | |
for (let name in descriptor){ | |
let fn = descriptor[name]; | |
handles[name] = Tracker.autorun(function(c){ | |
results[name] = fn(...args); | |
if (!c.firstRun) onData(results); | |
}); | |
} | |
onData(results); | |
return { | |
stop(){ | |
for(let name in handles){ | |
handles[name].stop(); | |
} | |
} | |
} | |
} | |
return connect(wrappedFn,...selectors); | |
}; |
This file contains 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
TestMessage = function(props){ | |
return ( | |
<div> | |
<span>Name: {props.name}</span> | |
<br/> | |
<span>Message: {props.message}</span> | |
</div> | |
) | |
}; | |
TestMessage = connectWithDescriptor({ | |
// These computations are re-run independently. | |
// Because they don't have any selectors, they don't | |
// wont re-run when props change. | |
message() { | |
return Session.get('lastMessage') | |
}, | |
name() { | |
return Session.get('playerName') | |
} | |
})(TestMessage) | |
This file contains 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
Messages = function(props){ | |
/* todo- rendering */ | |
} | |
// Have just put these separately so they're described. | |
const userSelector = (props)=>props.user; | |
const limitSelector = (props)=>props.limit; | |
const dateSelector = (props)=>props.date; | |
// This function will re-run whenever the result of a | |
// selector changes. | |
// Or when the result of the query changes | |
Messages = connectWithTracker(function(user, limit, date){ | |
let messages = Messages.find({user: user, date: {$gte: date}}, {sort: {date: 1}, limit: limit}).fetch(); | |
let unreadMessageCount = messages.reduce((count, msg)=>(count + (msg.unread ? 1 : 0)), 0); | |
return {messages, unreadMessageCount}; | |
}, userSelector, limitSelector, dateSelector)(Messages) | |
This file contains 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
Messages = function(props){ | |
/* todo- rendering */ | |
} | |
// Have just put these separately so they're described. | |
const userSelector = (props)=>props.user; | |
const limitSelector = (props)=>props.limit; | |
const dateSelector = (props)=>props.date; | |
// This version will re-run when the selectors change | |
// But keeps streaming new values via `onData` | |
// This example would probably be better if we used some sort of immutable wrapper | |
Messages = connect(function(user, limit, date, onData){ | |
var unreadMessageCount = 0; | |
var messages = []; | |
var init = true; | |
const cursor = Messages.find({user: user, date: {$gte: date}}, {sort: {date: 1}, limit: limit}); | |
// By manually observing changes, we can just emit changes | |
const handle = cursor.observe({ | |
addedAt(message, atIndex, before){ | |
if (message.unread) unreadMessageCount++; | |
messages.splice(atIndex, 0, message); | |
if (!init) onData({messages, unreadMessageCount}); | |
}, | |
changedAt(message, oldMessage, atIndex){ | |
messages.splice(atIndex, 1, message); | |
if (message.unread !== oldMessage.unread){ | |
if (message.unread) unreadMessageCount++; | |
else unreadMessageCount--; | |
} | |
onData({messages, unreadMessageCount}) | |
}, | |
removedAt(message, atIndex){ | |
if (message.unread) unreadMessageCount--; | |
messages.splice(atIndex, 1); | |
onData({messages, unreadMessageCount}) | |
}, | |
movedTo(message, fromIndex, toIndex){ | |
messages.splice(fromIndex, 1); | |
messages.splice(toIndex, 0, message); | |
onData({messages, unreadMessageCount}); | |
} | |
}) | |
onData({messages, unreadMessageCount}); | |
return { | |
stop(){ | |
handle.stop(); | |
} | |
} | |
}, userSelector, limitSelector, dateSelector)(Messages) |
This file contains 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
// This version will re-run when the selectors change | |
// But keeps streaming new values from the query via `onData` | |
// This is using https://github.com/mindfront/meteor-immutable-observer | |
Messages = connect(function(user, limit, date, onData){ | |
const cursor = Messages.find({user: user, date: {$gte: date}}, {sort: {date: 1}, limit: limit}); | |
const observer = new ImmutableObserver.List(cursor); | |
const computation = Tracker.autorun(function(){ | |
// This inner computation will re-run whenever | |
// the immutable driver computes a new documents list. | |
const messages = observer.documents(); | |
onData({ | |
messages, | |
unreadMessageCount: messages.reduce((count, msg)=>(count + (msg.get('unread') ? 1 : 0)), 0) | |
}) | |
}); | |
return { | |
stop(){ | |
computation.stop(); | |
observer.stop(); | |
} | |
}; | |
}, userSelector, limitSelector, dateSelector)(Messages); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment