-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server rendering #57
Server rendering #57
Comments
Yes, absolutely. We have plans that will probably happen this week. Sent from my iPhone
|
There is a bunch of discussion in #38 that is relevant to server-side rendering. Let's continue that discussion here. |
Looking forward to this. Started digging through old issues and the code, but not sure where everyone is planning to start. Willing to help in any way and usually on IRC 👍 |
It throws an error in Node during initialization:
It's predictible, because there is no global @rpflorence, could you please inform about the status of server-side routing support? |
The major blocker for server-side rendering at this point is coming up with a sane strategy for fetching data that works on both the client and the server. Requirements are:
Prior art/ideas: |
@mjackson ugh, react-async is a pile, don't make people use threads to fetch data. |
@phated the fibers stuff is stupid, agreed. But I really like the simplicity of |
@mjackson I think the crux of async server rendering is that React.renderComponentToString is a sync operation so async things can't happen inside it, leading him to use threads. I think fetching async and passing the data into the component as initial props (sync) is the only way to do server rendering. |
@phated But he has Right now I'm thinking:
If components need to display "loading..." UI, they can check for |
Sounds like a good plan. I think I actually have a place in current code where I would need the |
@rpflorence @ericflo @petehunt Would love to get your thoughts here. |
@mjackson yeah, that's pretty much exactly what I'm thinking too, minus the lifecycle hook, though that is compelling. I think we should do everything but that, start using it, and then see if we ever think "shoot, that lifecycle hook sure would be nice right now!" |
@phated can you explain how you imagine using componentWillReceiveAsyncState? |
This all sounds great to me! Agree with @rpflorence that the lifecycle hook seems like an optional extra. Just to be clear, on the client side there'd be no need to switch the renderComponent call from React to Router, correct? Also, personally I'd name it something other than renderComponentToString if it acts different from React's method of the same name, and I'd remove the word "initial" from getInitialAsyncState, but this bikeshed is yours to paint :) |
@ericflo Yes. You'll still use |
Probably should gist or PR this but I want to see if I am even on the same track as everyone's thinking var App = React.createClass({
statics: {
getInitialAsyncState: function(cb){
setTimeout(function(){
cb(null, {
initial: 'state'
});
}, 100);
}
}
}); function renderComponentToStringAsync(component, cb){
var done = function(err, state){
if(err){
cb(err);
}
component.setState(state, function(){
var html = React.renderComponentToString(component);
cb(null, html);
});
};
component.getInitialAsyncState(done)
} |
I think I'd vote for promises over callbacks if we can. |
@spicyj I would too, but usually there's more push back the other way, concept remains the same. I am unsure if I captured the thought behind |
@phated yeah, that's the basic idea. I think Also, I think React will complain if we try and call |
|
Why is that a problem? |
Because you may need to fetch something to know what to render, then you may need to fetch again to render again, etc etc |
This is a fiddle I made a while ago which somewhat mirrors our internal stuff: http://jsfiddle.net/R88XR/ The gist of the idea is that data fetching may only be a function of the route so that data fetching is batched together and render is batched together (synchronously). So there's a static The data you fetch is accessed at |
@petehunt I see. Thanks for sharing the example. This would be so much easier if React's |
The approach in that fiddle works well for that example, but how does it scale to many nested components? Do you have to pass in all data from the top? |
So the way I'd do it is have this mixin on each handler (which I'd rename to "entry point" if I had my way, btw) and return promises instead of callbacks. Run all the promises in parallel and pass their results back in as If a child component needs to fetch data, it can provide its own |
side note: On Thu, Jul 17, 2014 at 1:34 PM, Pete Hunt [email protected] wrote:
|
@sbrekken good call! Server rendering also needs to ensure that the |
@rpflorence I have been pondering the problem of all the Stores being singletons on the Server Side. Where concurrent requests are both rendering different pages. Being on a singleton this would cause both to change route. Obviously the stores will need to change to be instances. I have an idea for how to enable components like https://gist.github.com/karlmikko/07dec76a7bed23f79bdb The gist allows a component to look up the This allows the |
@sbrekken Router.renderRoutesToString(routes, url).then(null, function(failReason) {
if (failReason.code === 500) {
res.send(500);
}
}); You simply fail a promise in your |
@rpflorence your example is grate for when page/application consist of a few components, but usually the case In flux, one might have controller-views governing any significant section of the page - means single route, such as /blog/:postId might have many "controller views", If comments box is a stand alone component, I can just drop it anywhere, lets say in the /photos/:photoId, I wish that would be a way, to render the whole hierarchy of async components, when all the hierarchy reports "ready", the html is generated with initial state. It would be better if the "server rendering" would return a handle that could be rendered to html in a latter time, but I have no idea how to achieve that. |
@yurynix It is a common idiom both on the server and the client to deserialize a url to a set of data for rendering. In traditional server frameworks you don't want your partial views hitting the database, you want to fetch data in expected places. It is no different here. As for sharing components, send the data in as a property to the We're all pretty settled on this api. Feel free to propose a new one. |
@yurynix I agree that it'd be great to be able to load data all the way down the component chain but that's... hard. At least as React is built atm as it expects rendering to be synchronous. |
@rpflorence Sending fetchDataPromise.then(function(data) {
return {
metadata: view.getMetadata ? view.getMetadata(data) : {},
markup: React.renderComponentToString(Application({view: view(data)})),
initialData: JSON.stringify(data)
};
}, function(error) {
return {
markup: React.renderComponentToString(Application({error: error}))
};
}).then(function(locals) {
res.send(template(locals));
}); It would be nicer being able to Another example would be a |
@sbrekken How would the async error be handled on the client? One would want to handle these in the same way on the server to keep your code as isomorphic as possible. |
I would expect the Possibly if you reject the promise ReactRouter could set the state with the error object. var Foo = React.createClass({
getInitialAsyncState:function(path, query, setState){
return new Promise(resolve, reject){
//if error setState({error:"oh no"});
}
},
render:function(){
if (this.state.error)
return <Error.information/>
if (!this.state.data)
return <div>Loading...</div>
return <div>{this.state.data}</div>
}
}); |
@sbrekken I imagine it working something like this, as posted in #181 (comment) with the ideas from #142 (comment) // lets say we're in express
app.get('*', function(req, res) {
Router.renderRoutesToString(routes, req.originalUrl).then(function(result) {
res.send(result.html);
}, function(result) {
if (result.NOT_FOUND)
res.send(404, result.html);
else if (result.REDIRECT)
res.redirect(result.redirectUrl);
else
// this would never happen in the client
res.send(500, result.message);
});
}); |
@rpflorence Looks good to me, how are you planning on generating |
@karlmikko As best I can figure,
Edit: I should also say that I think the whole |
@rpflorence Something 🐟'y with your comment link. |
haha, copy/paste fail, link has been updated |
@rpflorence In your example does a failed promise on the client with |
On the client there would be no failed promise, but yes, it goes to the closes <Routes>
<Route path="/" handler={App}/>
<Route path="/users/:userId" handler={User}>
<Route path="/users/:userId/profile" handler={Profile}/>
<Route path="/users/:userId/*" handler={NotFoundUser}/>
</Route>
<Route path="/*" handler={NotFound}/>
</Routes> ^ That works today. It also happens to give us a way on the server to know the route is not recognized by the app when rendering on the server, and so we can fail the render promise. |
With love ❤️ , is there any ETA on this perhaps? 😸 Think we jumped the gun a bit and become a little more dependent on this than we were expecting before we realized that this wasn't isomorphic just yet. We're currently trying to use Keep up the great work, guys! |
🎄 ? no idea ... but definitely before 🎃 |
😢 Looks like we'll have to transition to a more traditional router due to the high-visibility and timeline of our project. Good luck with this! I'll definitely be back in the future, personally. Maybe I'll be able to drag the rest of them back with me as well. Thanks @rpflorence 👍 |
@Swivelgames there is a an Open PR #181 that will enable this, I don't suspect there would be much difference to the API from what is there. |
👍 I'll definitely review it! |
Let's follow up in #181. |
Hey @ryanflorence, the same that ocurred with @th0r throw is ocurring here. That error makes a 500 throw on server routes...
Do you think that have anything to do in this case? |
Hello,
|
Route.spec.js contains a commented out test for server rendering but it seems, server rendering would at least require exposing
findMatches
in the main module.Any plans for reintroducing server rendering support?
The text was updated successfully, but these errors were encountered: