Hey all, James from the Sideburns project here. I got some exciting features from Sideburns we are making available today for React!
A lot of what we’ve been building is extremely effective for React on its own, and since fully rendering Blaze 1 may take a while longer, @timbrandin and I decided it would be a good idea to start releasing parts of the Sideburns project now for React. These are likely some of the core features that @gschmidt and @evanyou hinted at would be in the future official Meteor-enhanced React package–so we figured we’d pave the way for what we want it to look like, and perhaps save them some time.
So without further ado, here’s the TrackerReact
mixin you can add to your React components today:
meteor add ultimatejs:tracker-react
https://atmospherejs.com/ultimatejs/tracker-react
And here’s a quick glympse to beam you guys the general concept:
App = React.createClass({
mixins: [TrackerReact],
title() {
return Session.get('title');
},
render() {
return (
<div>
<h1>{this.title()}</h1>
</div>
);
}
});
Badaboom bada bing, it works lol. Just like you expect. With the super declarative, automated, built-in reactivity we’ve come to depend on, and likely take for granted until React came into the picture. This simple enhancement should ease a lot of pain in making the switch.
##INSTANT EXAMPLE APP:
Here’s example app to see it all in action with just a few commands:
git clone https://github.com/ultimatejs/tracker-react-todos-example.git
cd tracker-react-todos-example
meteor
##WHAT DOES IT DO?
It’s essentially an upgrade to what ReactMeteorData
offers. It’s “method/helper level reactivity” rather than having to freeze all your reactivity in getMeteorData
. This seemingly small adjustment brings you a lot closer to the automated reactivity you have come to know and love in Blaze. A lot of benefits do come with it:
- any method you want is automatically reactive.
- you can specify methods you don’t want to be reactive by prefixing them with an underscore
_
, and your events go in a map returned from theevents
method. Lifecycle handlers are obviously prevented from being reactive as well. - each method runs in its own autorun, so that they aren’t all re-run together.
- you no longer have to refer to
this.data.foo
to get your data. You can refer tothis.foo()
to get it, which is symmetrical to what you are doing in Spacebars. - not all your reactive sources need to run if they are not called at run/render time-for example, if they are in an if/else code path not taken, which mean your component is forced to update less.
I’m sure you will find it a nice addition to your React(+Meteor) development experience. Keep in mind it’s brand new. We are releasing it in part for you to find something wrong with it, edge cases, etc. Try to break it! That way we can get these parts solid so by the time Sideburns (“Blaze React”) is done, it will be closer to true completion and ready for primetime. But that said, these features and future features we’ll release are being built to standalone as welcome additions to your React workflow. We have a few things unrelated to Sideburns we’ll release as well. So don’t tag this away in your brain just as things related to keeping your old Blaze 1 code useable. That’s one goal–the second is making the ideal React package for Meteor; and likely in cooperation with MDG as well, i.e. to achieve similar stuff to what @gschmidt said was on their roadmap the other day.
Anyway, that’s all for now. Give it a run-through, use it, break it. Thanks in advance for helping us bridge the Blaze world we know to what will be a delightful React future. Long Live Meteor! Stay tuned for more coming out of the Sideburns and Blaze React project. Pull requests welcome.
-James
##…MORE FOR THOSE INTERESTED:
So some concern has been raised that this is not “The React Way.” Well, that’s not true. It’s called "Sideways Data Loading" and it’s exactly what getMeteorData
already does and is working its way to becoming a first class API built into React: https://github.com/facebook/react/issues/3398 .
In short, it’s just state
that comes from outside the component, LIKE RELAY, and isn’t assigned to the state
key. State itself is basically just “sideways data” that originates in the component itself. It’s important to recognize what’s really going on here. It’s not often that your root component is passed the entire state needed to trickle down the entire tree purely as props. In fact, it’s like never. Data/State must be accumulated along the way somehow.
When you research react more, you begin to see it’s all about the forceUpdate
method used internally, and by mixins like this one and ReactMeteorData
. React tries to guide you into using it sparingly. It’s mainly meant for library code, whereas to application developers it all feels automated. But in short, React forces a component to update when it wants to, and only the children of its branch are potentially re-rendered; the rest of the tree stays untouched. With pure child components whose props haven’t changed, the operation is extremely fast–that’s why state management is such a big concern in React. It gives you deep control of your rendering performance. But at the end of the day, the mechanism is easy:
- state map changed, forceUpdate with new state.
In our case it’s:
- autorun tells us reactive dependencies have changed, forceUpdate + return the new data from utilized helper methods in render call.
With that said, you’re going to want to be more careful with React than with Blaze with how many helpers you use. In fact, rather, you have the opportunity to be more careful with React. In Blaze you don’t have this opportunity, and in part, that’s why Blaze performs so poorly–it’s constantly doing background work to see whether it needs to manipulate the DOM, often without even manipulating the DOM, and possibly triggering side effects if your helpers aren’t purely accessing reactive data sources, i.e. if they change a global variable. …So, in React, you could be forcing updates every which way, across the whole tree, and likely React will render/perform just fine, just like Blaze performs fine most of the time. It’s my opinion that developers are essentially brainwashed into being scared to do the slightest thing that triggers additional updates, god forbid call “forceUpdate” myself! The reality is you can throw a hell of a lot at React and your app will perform just fine.
THE CONCLUSION: make use of this automation to enhance your development flow, and when you feel its time to optimize, reduce the # of calls to the same data sources (Session
variables, cursors, etc) to being in one place near the top, i.e. the highest parent component that requires it, and then pass it down as props. That way you don’t have the same portions of the tree forced to update multiple times. In fact, this is a feature @arunoda made available via additional syntax to Blaze with Blaze Plus. The only difference is in React it’s a first class operation that’s standard practice in your workflow. In Blaze (Minus) if you do the equivalent of forceUpdate
at the top of a branch, i.e. pass down reactive data sources from parent templates, templates unnecessarily re-render. Whereas, in React child components only re-render if they implemented a changed prop. It’s a lot more finegrained.
So in the context of using TrackerReact
, by moving your state–STATE COMING FROM REACTIVE HELPERS–to the top, and passing down what’s needed, you reduce rendering work. In Blaze, you can’t move reactive data sources to the top (instead of in the helpers of child templates) without a lot of re-renders. I point you to @arunoda’s Blaze Plus for more information on that: https://github.com/kadirahq/blaze-plus. I also urge you to start a React app example, and add componentWillMount
, componentWillUpdate
and other life cycle handlers to your component, filled with console.log
to see when everything triggers, and then call setState()
from the console to compare it to blaze’s corresponding handlers. You will have to assign your component to something like window.COMPONENT
in the componentWillMount
handler so you have access to it from the console.
###FINAL THOUGHTS:
All this brings me to my final point: Blaze’s real problem is Tracker. Tracker is constantly in the background, often going hey-wire. It’s running autorun functions non-stop essentially. Say you have a cron job that updates an aggregate field on a collection object–well all connected clients will unnecessarily re-run helpers that are currently returning that object or cursor that contains it. You may say, well use fields
. But the reality is the following:
- you guys find it a nuisance to pass all these damn props down all the time in React, right?
- well, passing props down has perfect parity with the purpose of
fields
in yourfind()
calls: - it limits how often your components have to update!
- we often dont use
fields
so we don’t have to constantly do this extra boilerplate work.
The reality for many developers is it’s not until the end until you optimize fields
. Most projects are lucky to have the resources to fund/maintain it to this point.
What both Blaze and React for Meteor needs–you heard it hear first–is: ***“property level cursors”***. We are working towards a future where you can call this.subscribe
in your React component, benefit from the automation of “method level reactivity,” [a few other yet to be mentioned things ;)], AND use Spacebars! What the Meteor + React needs is a feature, at possibly at the Spacebars transpiler level, to tell the find
cursors what props are actually used, and automatically fill those in as fields. That will greatly AND AUTOMATICALLY limit the amount of component updates.
What I’m also saying here is this: MDG’s announcement the other day didn’t get to the heart of the matter and in fact made some misconceptions. The misconception being that it’s not possible to render Blaze 1 automatically to React and you’ll have to [manually] “port” your code:
Interpretation: you will have to do work on all your packages to make them work in the new React-based future. It won’t be automated.
In the coming weeks, Sideburns will disprove that. And I’m going to incrementally roll out the constituent features for React, as I’m doing today. That said and for what it’s worth, it’s my guess MDG made this announcement in response to all the pressure we put on them regarding React in the initial thread @mitar started. And they then made this announcement before they were fully informed, just because they felt they had to say something. I really wouldn’t take that announcement too seriously–just to empathize with them: they’re in a tough situation; they’re like 4 years in, probably having burnt through more money than they would have liked before generating a cent, and they finally have their cash cow ready (Galaxy), and likely it’s the biggest technical feat of all the engineering they’ve done; and now all they can think about it is making it pay for itself so they can even continue providing us these awesome tools to begin with! For all we know, they’re insulating us from how close they are to having to close shop if Galaxy doesn’t turn a profit and quick! See what I’m saying. Anyway, the people that know the most about React are–um, the fine people over at our now biggest competitor lol, Facebook, who built it. So that’s not MDG. There’s a lot we all have to learn about the new React world. I wouldn’t count on any one person within the Meteor community or MDG itself knowing everything about it yet. React is shaping up to be a lot more flexible than you might think–and very likely flexible enough to render Blaze 1.
NEXT: I’d like to get back to the changes I think MDG, and really the whole community, should focus on, instead of just Blaze 2 in React Form (on a side note: nobody has to, because I’m basically already gonna do it for you guys).
…Ok, so, the “heart of the matter” is that the real problem with Blaze is Tracker! It’s constantly updating as I’ve said. Autoruns get tangled, etc (I’m not just talking “helpers” anymore). I personally think it’s a necessary evil, given the declarative automation it provides, but it brings along some challenges; challenges that MDG has failed to address or possibly even recognize. In terms of Blaze, the renderings keep happening because there is no “property level cursor.” What would that look like–this:
Posts.find().map(post => post.title.get())
instead of:
Posts.find().map(post => post.title)
However, in javascript you never type out that operation the first way, you always perform it the second way, BUT IN SPACEBARS (OR JSX) the transpiler generates code that looks like the first way! The information of what property cursors were used somehow works its way “backwards” to where you made the initial find()
call an automatically specifies the fields
option for you. Now the TrackerReact
mixin (i.e. what was launched today) will call forceUpdate
on components far less. In terms of Blaze, if Blaze had this, it will perform a hell of a lot better as well. We need to find a way to make the helper autoruns stop running when they aren’t needed! That’s the challenge, and this is the solution. That problem isn’t magically solved by moving to React. Solving this needs to be a first class priority just like the “reactive revelation” via autoruns was for the founding MDG team.
That will solve helpers, but we also need to automatically optimize our autoruns that we create outside of helpers. To do that we need different classes of Autoruns. Essentially an Autorun class or object we can define which lets you set various properties that configure the circumstances within which it will run, e.g. only on a certain route, with certain params, eg. for a certain user role, if dependent subscriptions are ready!, if certain session vars are set, etc. We need as many ways to box in our Autoruns as possible, to keep them from being triggered in response to imprecise dependencies. Tracker needs to become more fine grained just like how React renders components/templates in a more fine grained manner than Blaze. Again this is Tracker work. We need to invest in taking Tracker to the next level.
I got a lot of stuff to release for React in the coming weeks that will make the "Connected React Client" feel like butter for Meteor. What’s likely outside of my capabilities is “property level cursors.” Now, I don’t even know if it’s fully possible–but with enough elbow grease almost anything is; and the point is “property level cursors” is worth it for the aforementioned reasons. So it’s these Tracker upgrades that I suggest should be the focus of MDG’s upcoming client side work. “property level cursors” is likely a mission, whereas what I’m doing is basically the obvious next steps for React+Meteor if you spend time thinking about it. Anyway, that’s it for now, look for React to become more awesome week by week.