Reputation: 19889
I'm messing around with React in Meteor. There seem to be two ways of going about things...
Given the meteor leaderboard example, we have a list of players that render a name and a score.
The pure way of doing things is fetch all the players and pass into the playersList component which then divvies up to playerItem components. Everything is done with props and these templates are pure. Whenever a player changes, we can use Meteor's reactivity to re-render everything with React
Tracker.autorun =>
players = Players.find().fetch()
React.render(React.createElement(PlayersList, {players:players}), document.body)
(the function passed to autorun will rerun whenever a player changes)
This is pretty much exactly what Pete Hunt did in his Meteor-React presentation demo.
Now the other way of doing this which seems much more efficient and fine-grained, but not pure. That is to pass the PlayersList component a list of playerIds. Pass the PlayerItem component a playerId and have the PlayerItem fetch the specific player and render the players name and score from the component's state.
// inside the PlayerItem component
componentWillMount: =>
Tracker.autorun =>
this.setState('player', Players.findOne(this.props.playerId))
Now the PlayerItem is not pure. Whenever a player's score changes, only the PlayerItem representing that player will be re-rendered. If we make sure to pass the PlayersList only a list of playerIds, then the PlayersList component will only be re-rendered whenever a player is added or removed.
Tracker.autorun =>
playerIds = _.pluck('_id',Players.find({}, {fields:{_id:1}}).fetch())
React.render(React.createElement(PlayersList, {playerIds: playerIds}), document.body)
This makes sense because now we have fine-grained control over React's render cycle and we're not bombarding react with the entire state of every player whenever one thing changes. However, this doesnt seem in the spirit of the way React was designed. Now we have internal state everywhere and our components are not pure!
It is my impression that the way React wants me to do things is use global state and pure components, and just rely on the fact that React's reconciliation is super fast and efficient. This just makes me slightly uncomfortable because the non-pure way seems much more efficient.
Anyways, I just started with React, so although I read all the documentation, I'm not confident that I know what I'm talking about. So I'm wondering -- when should I stick with pure components and when is it ok to have non-pure components? Should I ever fetch data within a component or should I treat react strictly as a rendering engine?
Upvotes: 3
Views: 2347
Reputation: 86260
There's very little difference between React.render and doing it inside the top level component. Either way it updates the states or props, renders, and diffs the old/new virtual dom.
I would have PlayerList fetch all of the players, and pass those down to PlayerItem components. You should be binding to external apis in componentDidMount
, and make sure to unbind in componentWillUnmount
. The componentWillMount
function has a similar role to constructors: set up any non-state instance properties.
PlayerItem should implement shouldComponentUpdate for performance. You can do this when you have some free time, as it's an optimization that doesn't affect the rest of the code.
As a side note, you can implement a mixin that helps with the meteor bindings.
var MeteorMixin = {
getInitialState: function(){
return this.getUpdatedState();
},
componentDidMount: function(){
this._meteorMixinComputation = Tracker.autorun(function(){
this.setState(this.getUpdatedState());
}.bind(this));
},
componentWillUnmount: function(){
this._meteorMixinComputation.stop();
}
};
And then your component will look like:
mixins: [MeteorMixin],
getUpdatedState: -> {players: Players.find({}).fetch()}
render: ->
...
Disclaimer: I don't use meteor, so this might be way off.
Upvotes: 2