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

Support asynchronous server rendering (waiting for data before rendering) #1739

Open
fdecampredon opened this Issue Jun 24, 2014 · 130 comments

Comments

Projects
None yet
@fdecampredon

fdecampredon commented Jun 24, 2014

It would seriously ease the process of building something isomorphic if componentWillMount could return a promise and that react would delay rendering until that promise is resolved. I have seen attempt of doing something like that in react-router and rrouter, however giving this responsibility to each component instead of a router module would make more sense for me.

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Jun 24, 2014

Member

The main reason (I believe) that this doesn't exist already is that on the client side, you basically always want to show some sort of loading indicator instead of deferring rendering. (It would also make the code significantly more complex, but we can probably deal with that.)

Member

sophiebits commented Jun 24, 2014

The main reason (I believe) that this doesn't exist already is that on the client side, you basically always want to show some sort of loading indicator instead of deferring rendering. (It would also make the code significantly more complex, but we can probably deal with that.)

@fdecampredon

This comment has been minimized.

Show comment
Hide comment
@fdecampredon

fdecampredon Jun 25, 2014

There is 2 cases that I find hard to solve without that:

  • On the server side, if you want to fetch data before rendering you can't delegate data retrieval to components since you don't have the information of which component will be rendered
  • On the client side the first time you mount your application after receiving pre-rendered html, even if you have some sort of cache from data retrieved on the server, you might want to use async method to retrieve those data, and that would prevent react from reusing the html.

react-async solve those problems with fibers, and cache. That do the trick but in my point of view those are just hackish solutions to solve a problem that can only be solved in core.

fdecampredon commented Jun 25, 2014

There is 2 cases that I find hard to solve without that:

  • On the server side, if you want to fetch data before rendering you can't delegate data retrieval to components since you don't have the information of which component will be rendered
  • On the client side the first time you mount your application after receiving pre-rendered html, even if you have some sort of cache from data retrieved on the server, you might want to use async method to retrieve those data, and that would prevent react from reusing the html.

react-async solve those problems with fibers, and cache. That do the trick but in my point of view those are just hackish solutions to solve a problem that can only be solved in core.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 25, 2014

Contributor

Color me uninformed on this subject, @fdecampredon say that componentWillMount is async and you don't return anything immediately, what is React supposed to render until there is, nothing? If so, why not just return nothing in render if there is no data yet? (Yeah I get server-side) Also, what should happen if props change before componentWillMount fires?

Personally, it seems like it's wrong to dispatch async requests during componentWillMount unless the component really is an isolated black box and also implements a loading indicator. As far as I understand it, React components should not be mistaken for more conventional OOP instances. In the best case, a React component is tool for visualizing the data in props, if it's interactive then possibly also state. It's a view, not a view and model.

To my ears, that sounds like the problem, React components shouldn't be the ones dispatching the async requests, you fetch all the data and when that data is ready, only then do you call React.renderComponent. Same solution client-side and server-side. You also get the ability to abort with an outcome of your choice if any async request fail.

Feel free to dismiss me if I misunderstood something, but it seems that you're treating React components as view and model, when (it seems) they're meant as just the view.

Contributor

syranide commented Jun 25, 2014

Color me uninformed on this subject, @fdecampredon say that componentWillMount is async and you don't return anything immediately, what is React supposed to render until there is, nothing? If so, why not just return nothing in render if there is no data yet? (Yeah I get server-side) Also, what should happen if props change before componentWillMount fires?

Personally, it seems like it's wrong to dispatch async requests during componentWillMount unless the component really is an isolated black box and also implements a loading indicator. As far as I understand it, React components should not be mistaken for more conventional OOP instances. In the best case, a React component is tool for visualizing the data in props, if it's interactive then possibly also state. It's a view, not a view and model.

To my ears, that sounds like the problem, React components shouldn't be the ones dispatching the async requests, you fetch all the data and when that data is ready, only then do you call React.renderComponent. Same solution client-side and server-side. You also get the ability to abort with an outcome of your choice if any async request fail.

Feel free to dismiss me if I misunderstood something, but it seems that you're treating React components as view and model, when (it seems) they're meant as just the view.

@fdecampredon

This comment has been minimized.

Show comment
Hide comment
@fdecampredon

fdecampredon Jun 25, 2014

Color me uninformed on this subject, @fdecampredon say that componentWillMount is async and you don't return anything immediately, what is React supposed to render until there is, nothing? If so, why not just return nothing in render if there is no data yet? (Yeah I get server-side) Also, what should happen if props change before componentWillMount fires?

I must admit that I did not think about all the cases ^^.
This feature would be useful only the first time we mount the top level component, and on the server, it's true that otherwise In most cast you would want to display a loader indicator.

Personally, it seems like it's wrong to dispatch async requests during componentWillMount unless the component really is an isolated black box and also implements a loading indicator. As far as I understand it, React components should not be mistaken for more conventional OOP instances. In the best case, a React component is tool for visualizing the data in props, if it's interactive then possibly also state. It's a view, not a view and model.

In a way or in other you'll want that a 'top-level' component would be able to retrieve data, like it's done in the Flux sample.
In this sample things are pretty simple because retrieving the list of todo is a synchronous operation, if it was not, and in case of pre-rendering on the server, we would render a first time with no data (and loose the pre-rendered markup from server).

In the case of a simple application with one set of data displayed by one view hierarchy there is still not so much problem, you can preload data and still keep the synchronous property of your store.
Now in case of an application composed of multiple modules that you reuse across your application I would like to be able to treat those modules as separate applications that are able to 'subscribe' on different stores (that would be responsible for fetching data).

Perhaps That I get the things the wrong way but some discussion/sample around the web make me think there is something missing somewhere:

  • In some sample it seems to me that @petehunt tried to achieve something similar.
  • react-nested-router promote some similar mechanism in willTransitionTo, and some discussions make me feel that nobody has come with a proper solution.
  • RRouter also provides some kind of mechanism to prefetch data as the component is being rendered/mounted.
  • and finally react-async like I said earlier.

fdecampredon commented Jun 25, 2014

Color me uninformed on this subject, @fdecampredon say that componentWillMount is async and you don't return anything immediately, what is React supposed to render until there is, nothing? If so, why not just return nothing in render if there is no data yet? (Yeah I get server-side) Also, what should happen if props change before componentWillMount fires?

I must admit that I did not think about all the cases ^^.
This feature would be useful only the first time we mount the top level component, and on the server, it's true that otherwise In most cast you would want to display a loader indicator.

Personally, it seems like it's wrong to dispatch async requests during componentWillMount unless the component really is an isolated black box and also implements a loading indicator. As far as I understand it, React components should not be mistaken for more conventional OOP instances. In the best case, a React component is tool for visualizing the data in props, if it's interactive then possibly also state. It's a view, not a view and model.

In a way or in other you'll want that a 'top-level' component would be able to retrieve data, like it's done in the Flux sample.
In this sample things are pretty simple because retrieving the list of todo is a synchronous operation, if it was not, and in case of pre-rendering on the server, we would render a first time with no data (and loose the pre-rendered markup from server).

In the case of a simple application with one set of data displayed by one view hierarchy there is still not so much problem, you can preload data and still keep the synchronous property of your store.
Now in case of an application composed of multiple modules that you reuse across your application I would like to be able to treat those modules as separate applications that are able to 'subscribe' on different stores (that would be responsible for fetching data).

Perhaps That I get the things the wrong way but some discussion/sample around the web make me think there is something missing somewhere:

  • In some sample it seems to me that @petehunt tried to achieve something similar.
  • react-nested-router promote some similar mechanism in willTransitionTo, and some discussions make me feel that nobody has come with a proper solution.
  • RRouter also provides some kind of mechanism to prefetch data as the component is being rendered/mounted.
  • and finally react-async like I said earlier.
@mjackson

This comment has been minimized.

Show comment
Hide comment
@mjackson

mjackson Jun 26, 2014

Contributor

@fdecampredon To be clear, the purpose of willTransitionTo in react-nested-router is not for loading data, specifically because returning a promise from that method will indeed block rendering new UI, which you don't want to do unless you absolutely have to.

Contributor

mjackson commented Jun 26, 2014

@fdecampredon To be clear, the purpose of willTransitionTo in react-nested-router is not for loading data, specifically because returning a promise from that method will indeed block rendering new UI, which you don't want to do unless you absolutely have to.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 26, 2014

Contributor

@fdecampredon Everyone is still trying to figure things out, so it wouldn't surprise me if no one has a definitive answer. But I'm guessing the devs at Facebook must've run into this themselves a few times.

Contributor

syranide commented Jun 26, 2014

@fdecampredon Everyone is still trying to figure things out, so it wouldn't surprise me if no one has a definitive answer. But I'm guessing the devs at Facebook must've run into this themselves a few times.

@tobice

This comment has been minimized.

Show comment
Hide comment
@tobice

tobice Oct 2, 2014

Any update on this? I've just started exploring React and I immediately run into this. Many people recommend React as a go to solution for building isomorphic apps, but as long as this is not solved I think it simply can't get the job done.

To my ears, that sounds like the problem, React components shouldn't be the ones dispatching the async requests, you fetch all the data and when that data is ready, only then do you call React.renderComponent. Same solution client-side and server-side. You also get the ability to abort with an outcome of your choice if any async request fail.

Feel free to dismiss me if I misunderstood something, but it seems that you're treating React components as view and model, when (it seems) they're meant as just the view.

If this is true then React is nothing more than a slightly different templating solution / view layer. And that would be a shame because there is such a potential. I really understand @fdecampredon when he mentions those complex applications composed of multiple modules. React would be perfect for this.

I don't think that this approach would mean treating a component as view and model. If you look at the Flux architecture, they think of React components as of controller-views that not only display data (from stores) but also invoke actions based on user interactions. And the actions then update the stores (= model). To me it sounds just like an obvious MVC architecture.

The problem is with the initial action that populates the store. It's fairly simple on the client side where the action can be invoked from the componentDidMount() method as recommended. On the server side on the other hand, we really need some special place and some kind mechanism that would delay the rendering until the action is finished and the stores are populated.

At this moment, it seems to me that the only way is React-async/Fibers + Flux. The nice thing about Flux is that we don't need some artificial cache to transport component states from the server to the client (like it's done in the original react-async example), we can simply initialize the stores and then send them to the client with the html markup (see this example).

But this solution is indeed hackish.

tobice commented Oct 2, 2014

Any update on this? I've just started exploring React and I immediately run into this. Many people recommend React as a go to solution for building isomorphic apps, but as long as this is not solved I think it simply can't get the job done.

To my ears, that sounds like the problem, React components shouldn't be the ones dispatching the async requests, you fetch all the data and when that data is ready, only then do you call React.renderComponent. Same solution client-side and server-side. You also get the ability to abort with an outcome of your choice if any async request fail.

Feel free to dismiss me if I misunderstood something, but it seems that you're treating React components as view and model, when (it seems) they're meant as just the view.

If this is true then React is nothing more than a slightly different templating solution / view layer. And that would be a shame because there is such a potential. I really understand @fdecampredon when he mentions those complex applications composed of multiple modules. React would be perfect for this.

I don't think that this approach would mean treating a component as view and model. If you look at the Flux architecture, they think of React components as of controller-views that not only display data (from stores) but also invoke actions based on user interactions. And the actions then update the stores (= model). To me it sounds just like an obvious MVC architecture.

The problem is with the initial action that populates the store. It's fairly simple on the client side where the action can be invoked from the componentDidMount() method as recommended. On the server side on the other hand, we really need some special place and some kind mechanism that would delay the rendering until the action is finished and the stores are populated.

At this moment, it seems to me that the only way is React-async/Fibers + Flux. The nice thing about Flux is that we don't need some artificial cache to transport component states from the server to the client (like it's done in the original react-async example), we can simply initialize the stores and then send them to the client with the html markup (see this example).

But this solution is indeed hackish.

@mridgway

This comment has been minimized.

Show comment
Hide comment
@mridgway

mridgway Oct 30, 2014

Contributor

I don't think it necessarily needs to be componentWillMount that is async; I'm not even sure it needs to be a lifecycle event. The real issue is that on the server side there is no way to analyze the component tree until after everything has been rendered to a string.

My ideal solution to solve this would be to allow "rendering" that would just build the component tree, then we would be able to traverse the tree to find components that need additional data, allow us to asynchronously load more data and "re-render" that components subtree, and then once we are ready for flushing the markup, allow us to convert that tree to a string.

This replicates what we're able to do in the browser: have a virtual DOM that we can re-render as we want. The difference is that in the browser DOM updates can be implicit. On the server we need to be explicit about when we render to a string so that we can perform updates to the virtual DOM based on async data.

Contributor

mridgway commented Oct 30, 2014

I don't think it necessarily needs to be componentWillMount that is async; I'm not even sure it needs to be a lifecycle event. The real issue is that on the server side there is no way to analyze the component tree until after everything has been rendered to a string.

My ideal solution to solve this would be to allow "rendering" that would just build the component tree, then we would be able to traverse the tree to find components that need additional data, allow us to asynchronously load more data and "re-render" that components subtree, and then once we are ready for flushing the markup, allow us to convert that tree to a string.

This replicates what we're able to do in the browser: have a virtual DOM that we can re-render as we want. The difference is that in the browser DOM updates can be implicit. On the server we need to be explicit about when we render to a string so that we can perform updates to the virtual DOM based on async data.

@koistya

This comment has been minimized.

Show comment
Hide comment
@koistya

koistya Nov 11, 2014

Contributor
My ideal solution to solve this would be to allow "rendering" that would just build the component tree, then we would be able to traverse the tree to find components that need additional data, allow us to asynchronously load more data and "re-render" that components subtree, and then once we are ready for flushing the markup, allow us to convert that tree to a string. — @mridgway

Yep, that would be good. Currently, rendering component twice on a server-side can be used as a workaround.

Contributor

koistya commented Nov 11, 2014

My ideal solution to solve this would be to allow "rendering" that would just build the component tree, then we would be able to traverse the tree to find components that need additional data, allow us to asynchronously load more data and "re-render" that components subtree, and then once we are ready for flushing the markup, allow us to convert that tree to a string. — @mridgway

Yep, that would be good. Currently, rendering component twice on a server-side can be used as a workaround.

@mridgway

This comment has been minimized.

Show comment
Hide comment
@mridgway

mridgway Feb 27, 2015

Contributor

I want to reference react-nexus as an example of what I'd want to be supported within React. It's essentially a rewrite of how mountComponent works except that it builds out the component tree without actually mounting it to the DOM or writing out a string. This allows you to traverse the component tree and fire off asynchronous methods while the tree is being built up. The issue with this implementation as it is, is that it throws away that first tree and then calls React.renderToString anyway, whereas it would be nice to take that pre-render tree and render/mount it.

I'm willing to work on this within core, but would need some pointers. I think one of the requirements is making ReactContext not globally referenced so that async wouldn't cause issues. Are there other globals that might run into issues with this as well?

Contributor

mridgway commented Feb 27, 2015

I want to reference react-nexus as an example of what I'd want to be supported within React. It's essentially a rewrite of how mountComponent works except that it builds out the component tree without actually mounting it to the DOM or writing out a string. This allows you to traverse the component tree and fire off asynchronous methods while the tree is being built up. The issue with this implementation as it is, is that it throws away that first tree and then calls React.renderToString anyway, whereas it would be nice to take that pre-render tree and render/mount it.

I'm willing to work on this within core, but would need some pointers. I think one of the requirements is making ReactContext not globally referenced so that async wouldn't cause issues. Are there other globals that might run into issues with this as well?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Feb 27, 2015

Member

@mridgway If I'm not mistaken global ReactContext might be a compat thing and will go away in 0.14. But I might be wrong.

Member

gaearon commented Feb 27, 2015

@mridgway If I'm not mistaken global ReactContext might be a compat thing and will go away in 0.14. But I might be wrong.

@mridgway

This comment has been minimized.

Show comment
Hide comment
@mridgway

mridgway Feb 27, 2015

Contributor

@gaearon Yeah, that's the sense I got from the withContext deprecation.

Contributor

mridgway commented Feb 27, 2015

@gaearon Yeah, that's the sense I got from the withContext deprecation.

@SpainTrain

This comment has been minimized.

Show comment
Hide comment
@SpainTrain

SpainTrain Jun 22, 2015

👍 Using react-router for this ATM. @mridgway's approach sounds very reasonable.

SpainTrain commented Jun 22, 2015

👍 Using react-router for this ATM. @mridgway's approach sounds very reasonable.

@anatomic

This comment has been minimized.

Show comment
Hide comment
@anatomic

anatomic Jun 29, 2015

A slightly different take on this issue is how to handle asynchronous loading of code chunks produced by a bundler (such as webpack).

As discussed in this ticket - ReactTraining/react-router#1402 - the issue with async rendering support is the initial render appears to be null as the relevant chunks haven't yet loaded in the first render pass, resulting in a <noscript> at the root of the DOM and a checksum fail. Everything falls into place quickly afterwards, but it does result in a flicker in the UI when working locally and worse in the field, especially if the chunks to be downloaded are of a reasonable size (say > 30KB)

The ability to split up big apps into multiple chunks is important to us and having solved the data fetching issue (we used react-router and nested routes which can be traversed prior to rendering on the server to fetch data dependencies) this is the last piece of the puzzle blocking us from moving fully to a React solution for our front-end.

anatomic commented Jun 29, 2015

A slightly different take on this issue is how to handle asynchronous loading of code chunks produced by a bundler (such as webpack).

As discussed in this ticket - ReactTraining/react-router#1402 - the issue with async rendering support is the initial render appears to be null as the relevant chunks haven't yet loaded in the first render pass, resulting in a <noscript> at the root of the DOM and a checksum fail. Everything falls into place quickly afterwards, but it does result in a flicker in the UI when working locally and worse in the field, especially if the chunks to be downloaded are of a reasonable size (say > 30KB)

The ability to split up big apps into multiple chunks is important to us and having solved the data fetching issue (we used react-router and nested routes which can be traversed prior to rendering on the server to fetch data dependencies) this is the last piece of the puzzle blocking us from moving fully to a React solution for our front-end.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 29, 2015

Contributor

@anatomic That's not React's responsibility, it's your job to chunk appropriately and defer rendering until all necessary chunks has been loaded. To put it differently, if one of your components has a dependency on some external library, it's obviously your problem to satisify before trying to use it, React couldn't do it even if it tried, so the same applies across the board.

Feel free to implement alternative strategies that may suit you better, say <WaitFor for={MyAsyncLoadedCompSignal} until={...} then={() => <MyAsyncLoadedComp ... />} />. But these are inherently opinionated and not something React should or even needs to offer, so it's best left to the community.

Contributor

syranide commented Jun 29, 2015

@anatomic That's not React's responsibility, it's your job to chunk appropriately and defer rendering until all necessary chunks has been loaded. To put it differently, if one of your components has a dependency on some external library, it's obviously your problem to satisify before trying to use it, React couldn't do it even if it tried, so the same applies across the board.

Feel free to implement alternative strategies that may suit you better, say <WaitFor for={MyAsyncLoadedCompSignal} until={...} then={() => <MyAsyncLoadedComp ... />} />. But these are inherently opinionated and not something React should or even needs to offer, so it's best left to the community.

@koistya

This comment has been minimized.

Show comment
Hide comment
@koistya

koistya Jun 29, 2015

Contributor

It's better to keep async stuff outside the scope of React components, here is an example:

import React from 'react';
import Layout from './components/Layout';
import NotFoundPage from './components/NotFoundPage';
import ErrorPage from './components/ErrorPage';

const routes = {

  '/': () => new Promise(resolve => {
    require(['./components/HomePage'], HomePage => { // Webpack's script loader
      resolve(<Layout><HomePage /></Layout>);
    });
  }),

  '/about': () => new Promise(resolve => {
    require(['./components/AboutPage'], AboutPage => { // Webpack's script loader
      resolve(<Layout><AboutPage /></Layout>);
    });
  })

};

const container = document.getElementById('app');

async function render() {
  try {
    const path = window.location.hash.substr(1) || '/';
    const route = routes[path];
    const component = route ? await route() : <NotFoundPage />;
    React.render(component, container);
  } catch (err) {
    React.render(<ErrorPage error={err} />, container);
  }
}

window.addEventListener('hashchange', () => render());
render();

See Webpack Code Splitting, React.js Routing from Scratch and react-routing (coming soon)

Contributor

koistya commented Jun 29, 2015

It's better to keep async stuff outside the scope of React components, here is an example:

import React from 'react';
import Layout from './components/Layout';
import NotFoundPage from './components/NotFoundPage';
import ErrorPage from './components/ErrorPage';

const routes = {

  '/': () => new Promise(resolve => {
    require(['./components/HomePage'], HomePage => { // Webpack's script loader
      resolve(<Layout><HomePage /></Layout>);
    });
  }),

  '/about': () => new Promise(resolve => {
    require(['./components/AboutPage'], AboutPage => { // Webpack's script loader
      resolve(<Layout><AboutPage /></Layout>);
    });
  })

};

const container = document.getElementById('app');

async function render() {
  try {
    const path = window.location.hash.substr(1) || '/';
    const route = routes[path];
    const component = route ? await route() : <NotFoundPage />;
    React.render(component, container);
  } catch (err) {
    React.render(<ErrorPage error={err} />, container);
  }
}

window.addEventListener('hashchange', () => render());
render();

See Webpack Code Splitting, React.js Routing from Scratch and react-routing (coming soon)

@anatomic

This comment has been minimized.

Show comment
Hide comment
@anatomic

anatomic Jun 29, 2015

@syranide I'll keep working away at it, but I don't think it's as binary as you've put it above. We are using react-router so that may introduce some issues to the mix as the router is a component rather than sitting outside the React environment.

If we did the <WaitFor ... /> approach, surely we'll still get a different DOM on the first render which will still cause the flicker/disappearance of content?

anatomic commented Jun 29, 2015

@syranide I'll keep working away at it, but I don't think it's as binary as you've put it above. We are using react-router so that may introduce some issues to the mix as the router is a component rather than sitting outside the React environment.

If we did the <WaitFor ... /> approach, surely we'll still get a different DOM on the first render which will still cause the flicker/disappearance of content?

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 29, 2015

Contributor

If we did the <WaitFor ... /> approach, surely we'll still get a different DOM on the first render which will still cause the flicker/disappearance of content?

If you don't want flicker (some do) it's simply a matter of waiting for all the chunks you depend on to have loaded before rendering, webpack provides this out-of-the-box with require.resolve.

PS. Yes, react-router and whatever else you're using surely complicates the solution, but it's still not React's problem to solve.

Contributor

syranide commented Jun 29, 2015

If we did the <WaitFor ... /> approach, surely we'll still get a different DOM on the first render which will still cause the flicker/disappearance of content?

If you don't want flicker (some do) it's simply a matter of waiting for all the chunks you depend on to have loaded before rendering, webpack provides this out-of-the-box with require.resolve.

PS. Yes, react-router and whatever else you're using surely complicates the solution, but it's still not React's problem to solve.

@anatomic

This comment has been minimized.

Show comment
Hide comment
@anatomic

anatomic Jun 29, 2015

If you don't want flicker (some do) it's simply a matter of waiting for all the chunks you depend on to have loaded before rendering, webpack provides this out-of-the-box with require.resolve.

I'll look into that, my understanding of require.resolve was that it was a synchronous lookup of a module's id and didn't involve a trip to the server? We're using require.ensure to manage our chunk loading.

Looking at our code again I think we can circumnavigate the problem by making react-router think it's rendering on the server but then following the standard client-side approach:

const history = new BrowserHistory();

if (typeof history.setup === "function") {
    history.setup();
}

Router.run(routes, history.location, (err, initialState, transition) => {
    React.render(<Provider redux={redux}>{() => <Router key="ta-app" history={history} children={routes} />}</Provider>, document.getElementById('ta-app'));
});

anatomic commented Jun 29, 2015

If you don't want flicker (some do) it's simply a matter of waiting for all the chunks you depend on to have loaded before rendering, webpack provides this out-of-the-box with require.resolve.

I'll look into that, my understanding of require.resolve was that it was a synchronous lookup of a module's id and didn't involve a trip to the server? We're using require.ensure to manage our chunk loading.

Looking at our code again I think we can circumnavigate the problem by making react-router think it's rendering on the server but then following the standard client-side approach:

const history = new BrowserHistory();

if (typeof history.setup === "function") {
    history.setup();
}

Router.run(routes, history.location, (err, initialState, transition) => {
    React.render(<Provider redux={redux}>{() => <Router key="ta-app" history={history} children={routes} />}</Provider>, document.getElementById('ta-app'));
});
@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 29, 2015

Contributor

I'll look into that, my understanding of require.resolve was that it was a synchronous lookup of a module's id and didn't involve a trip to the server? We're using require.ensure to manage our chunk loading.

Sorry, yes I meant require.ensure. The callback is executed only when all the dependencies are satisfied, so it's a matter of just putting render/setState inside of it.

Contributor

syranide commented Jun 29, 2015

I'll look into that, my understanding of require.resolve was that it was a synchronous lookup of a module's id and didn't involve a trip to the server? We're using require.ensure to manage our chunk loading.

Sorry, yes I meant require.ensure. The callback is executed only when all the dependencies are satisfied, so it's a matter of just putting render/setState inside of it.

@anatomic

This comment has been minimized.

Show comment
Hide comment
@anatomic

anatomic Jun 29, 2015

Ok, that's kind of how we were doing it, thanks for your replies. This feels like something that needs to be addressed in react-router so I'll carry on the discussion there - sorry if this has been the wrong place to have this conversation!

You are right that the require.ensure is the way to go, I guess our ultimate issue was that it should be linked to the route currently being visited so is directly tied to the router. With react-router being component based that ties it to the render tree. Without my hack above we're left fighting a way to view the tree before everything is loaded async (or duplicating the routing logic to allow for relevant chunks to be loaded at the top level).

anatomic commented Jun 29, 2015

Ok, that's kind of how we were doing it, thanks for your replies. This feels like something that needs to be addressed in react-router so I'll carry on the discussion there - sorry if this has been the wrong place to have this conversation!

You are right that the require.ensure is the way to go, I guess our ultimate issue was that it should be linked to the route currently being visited so is directly tied to the router. With react-router being component based that ties it to the render tree. Without my hack above we're left fighting a way to view the tree before everything is loaded async (or duplicating the routing logic to allow for relevant chunks to be loaded at the top level).

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jun 29, 2015

Contributor

You are right that the require.ensure is the way to go, I guess our ultimate issue was that it should be linked to the route currently being visited so is directly tied to the router. With react-router being component based that ties it to the render tree. Without my hack above we're left fighting a way to view the tree before everything is loaded async (or duplicating the routing logic to allow for relevant chunks to be loaded at the top level).

I'm not intimately familiar with react-router, but I still imagine it simply being a case of setRoute(...) => require.ensure([], function() { require(...); setRoute(...); }) which really isn't practical so you introduce another level of indirection. A map of routes and the async require.ensure loader for each. Write a helper function asyncSetRoute(...) { loadRoute(route, function() { setRoute(...); }}, now you call asyncSetRoute instead and it will defer updating the router until everything is ready.

Pseudo-code and kind of generic, but that seems like the overall approach to me. Maybe react-router should provide this, maybe not... maybe it's ideally provided as an external helper, I'm not sure.

Contributor

syranide commented Jun 29, 2015

You are right that the require.ensure is the way to go, I guess our ultimate issue was that it should be linked to the route currently being visited so is directly tied to the router. With react-router being component based that ties it to the render tree. Without my hack above we're left fighting a way to view the tree before everything is loaded async (or duplicating the routing logic to allow for relevant chunks to be loaded at the top level).

I'm not intimately familiar with react-router, but I still imagine it simply being a case of setRoute(...) => require.ensure([], function() { require(...); setRoute(...); }) which really isn't practical so you introduce another level of indirection. A map of routes and the async require.ensure loader for each. Write a helper function asyncSetRoute(...) { loadRoute(route, function() { setRoute(...); }}, now you call asyncSetRoute instead and it will defer updating the router until everything is ready.

Pseudo-code and kind of generic, but that seems like the overall approach to me. Maybe react-router should provide this, maybe not... maybe it's ideally provided as an external helper, I'm not sure.

@sophiebits sophiebits changed the title from Make componentWillMount async. to Support asynchronous server rendering (waiting for data before rendering) Aug 12, 2015

@NickStefan

This comment has been minimized.

Show comment
Hide comment
@NickStefan

NickStefan Aug 25, 2015

So after hours of research, I'm just now confirming that server side rendering is _impossible_ unless you feed everything from the top down (?).

Possible short term solutions:
A. Pre-fill a store and make server side loading synchronous

B. Feed everything from the top as one data input after async fetching your one data object.

re: 'A'. This wont work unless you already know what your render structure will look like. I need to render it in order to know my various collections/models/api dependencies. Also, how do I have the fetching be async on the client, but sync on the server, without having two different APIs?

re: 'B'. Basically the same problem as above. Are people having to make little dependency JSONs to go with each of their routes?

Are there any other short term solutions while we wait for a React supported solution? Any user land forks or plugins? https://www.npmjs.com/package/react-async ?

NickStefan commented Aug 25, 2015

So after hours of research, I'm just now confirming that server side rendering is _impossible_ unless you feed everything from the top down (?).

Possible short term solutions:
A. Pre-fill a store and make server side loading synchronous

B. Feed everything from the top as one data input after async fetching your one data object.

re: 'A'. This wont work unless you already know what your render structure will look like. I need to render it in order to know my various collections/models/api dependencies. Also, how do I have the fetching be async on the client, but sync on the server, without having two different APIs?

re: 'B'. Basically the same problem as above. Are people having to make little dependency JSONs to go with each of their routes?

Are there any other short term solutions while we wait for a React supported solution? Any user land forks or plugins? https://www.npmjs.com/package/react-async ?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 25, 2015

Member

@NickStefan

I don't understand the problem. :-(

  1. Use Flux or Flux-like container (Alt, Redux, Flummox, etc) where stores aren't singletons.
  2. Define static methods like fetchData on your route handlers that return promises.
  3. On server, match route to components, grab fetchData from them and wait for them to complete before rendering. This will prefill your Flux or Redux store instance. Note that store instance is not a singleton—it's bound to a particular request, so requests stay isolated.
  4. When the promises are ready, render synchronously with the store instance you just prefilled.

Here's a good tutorial for Redux describing this approach: https://medium.com/@bananaoomarang/handcrafting-an-isomorphic-redux-application-with-love-40ada4468af4

Member

gaearon commented Aug 25, 2015

@NickStefan

I don't understand the problem. :-(

  1. Use Flux or Flux-like container (Alt, Redux, Flummox, etc) where stores aren't singletons.
  2. Define static methods like fetchData on your route handlers that return promises.
  3. On server, match route to components, grab fetchData from them and wait for them to complete before rendering. This will prefill your Flux or Redux store instance. Note that store instance is not a singleton—it's bound to a particular request, so requests stay isolated.
  4. When the promises are ready, render synchronously with the store instance you just prefilled.

Here's a good tutorial for Redux describing this approach: https://medium.com/@bananaoomarang/handcrafting-an-isomorphic-redux-application-with-love-40ada4468af4

@NickStefan

This comment has been minimized.

Show comment
Hide comment
@NickStefan

NickStefan Aug 25, 2015

@gaearon I apologize if I confused you. Thank you for response. From your list, it sounds like I am correct in assuming the solution to server data dependency is to only ever define data needs at your root component (static methods / the article you linked to). If your data dependencies are defined at the root, it's much easier to pre-fill stores etc.

This is good flux practice, but isn't it potentially limiting? If I add a small component at the bottom of your view tree that needs some very different data, I then need to go edit the data dependencies at the root.

What I'm asking for is a way for deeply nested components to define asynchronous data needs.

If I have to add their needs to the root component, aren't I coupling the root to that one sub components needs?

NickStefan commented Aug 25, 2015

@gaearon I apologize if I confused you. Thank you for response. From your list, it sounds like I am correct in assuming the solution to server data dependency is to only ever define data needs at your root component (static methods / the article you linked to). If your data dependencies are defined at the root, it's much easier to pre-fill stores etc.

This is good flux practice, but isn't it potentially limiting? If I add a small component at the bottom of your view tree that needs some very different data, I then need to go edit the data dependencies at the root.

What I'm asking for is a way for deeply nested components to define asynchronous data needs.

If I have to add their needs to the root component, aren't I coupling the root to that one sub components needs?

@koistya

This comment has been minimized.

Show comment
Hide comment
@koistya

koistya Aug 26, 2015

Contributor

@NickStefan with react-routing, for example, the async data fetching looks like this:

import Router from 'react-routing/lib/Router';
import http from './core/http';

const router = new Router(on => {
  on('/products', async () => <ProductList />);
  on('/products/:id', async (state) => {
    const data = await http.get(`/api/products/${state.params.id`);
    return data && <Product {...data} />;
  });
});

await router.dispatch('/products/123', component => React.render(component, document.body));
Contributor

koistya commented Aug 26, 2015

@NickStefan with react-routing, for example, the async data fetching looks like this:

import Router from 'react-routing/lib/Router';
import http from './core/http';

const router = new Router(on => {
  on('/products', async () => <ProductList />);
  on('/products/:id', async (state) => {
    const data = await http.get(`/api/products/${state.params.id`);
    return data && <Product {...data} />;
  });
});

await router.dispatch('/products/123', component => React.render(component, document.body));
@3axap4eHko

This comment has been minimized.

Show comment
Hide comment
@3axap4eHko

3axap4eHko Sep 8, 2017

I think the easiest way will be supporting async render method (render returns a promise). It will allow root component wait children components rendering and of course result of render will be a promise. I implemented something like this in preact here https://github.com/3axap4eHko/preact-async-example

3axap4eHko commented Sep 8, 2017

I think the easiest way will be supporting async render method (render returns a promise). It will allow root component wait children components rendering and of course result of render will be a promise. I implemented something like this in preact here https://github.com/3axap4eHko/preact-async-example

@raRaRa

This comment has been minimized.

Show comment
Hide comment
@raRaRa

raRaRa Sep 22, 2017

@faceyspacey Thank you for the detailed answer above. I really like the idea of Redux-First Router, it definitely solves all of my problems, and makes things much more transparent and clean than what we had before.

Big thanks!

raRaRa commented Sep 22, 2017

@faceyspacey Thank you for the detailed answer above. I really like the idea of Redux-First Router, it definitely solves all of my problems, and makes things much more transparent and clean than what we had before.

Big thanks!

@faceyspacey

This comment has been minimized.

Show comment
Hide comment
@faceyspacey

faceyspacey Sep 22, 2017

@raRaRa glad you like it! ...one of the biggest challenges is there hasn't been a good abstraction for route level data-fetching until now. React React v3 had callbacks, and the v4 has an excellent pattern (particularly with the react-router-config package), but it's non-obvious and not first-class in the v4. It's an after-thought. More importantly however, it doesn't live in Redux. So it's only really now for the first time that redux users have a complete "route level" abstraction.

I think now with RFR, we're gonna see a re-thinking of the importance of "route level" vs "component level." Things like Relay and Apollo have created a lot of hype around pairing data dependencies to components, but if you aren't using those 2, you're likely going to end up in a world of hurt at some point by doing things in lifecycle handlers.

That said eventually RFR will have a route level integration with Apollo. There's just very little benefits for doing things at the component level, minus the aforementioned rare exceptions. At least 80% of the time (and perhaps 100% in your app), you can determine all the data you need based on the route. If it's not possible, it's likely because you're deriving data in your "view layer" that very well could be re-thought to all be interpreted by the URL. So even with tools like Apollo you begin to engage in problematic anti-patterns that will likely eventually cause you pain by letting "state" live nowhere else but the view layer. I.e. the state necessary to perform those data-fetchings. You often have to refine the exact parameters used to fetch your data. So you take state and props and transform the 2 to get the params just prior to fetching the data. That's stuff that now lives in the view layer and subject to its re-rendering mechanisms (i.e. when lifecycle handlers are called). Every app at one point or another ends up with the data not fetched when it should be or fetched in response to unrelated props received/changed. And in all apps, you have to write extra code to guard against unrelated props that have changed. I.e. props that don't affect the params used to fetch data.

As for React Native + Apollo, there the problem is less because you don't have SSR and therefore potential for sequential data-fetchings happening on the server. However, do you really need component level data-fetching when screens are so small. You generally know what each "scene" needs in terms of its data. It's not like a desktop web panel dashboard with a million graphs, multiple tables, etc. That one use case is perhaps the biggest primary place where component level starts to make sense. But even there you can specify all your requirements for all those widgets in one place and fetch them via Apollo. I haven't given it enough thought yet, but the basic idea is to attach your GraphQL data-requirements to a route, and then in your components connect to the data just like any other Redux data. So more specifically, the idea is to eliminate having both react-redux's connect and Apollo's equivalent. I just want one, and we want it to be flatter. Using Apollo requires destructuring more deeply nested data to gain access to what you truly want in your components. The vision is one way that looks just like regular Redux, plus graphql specifications at the route level.

It's a bit premature to be talkin about it, as I have barely looked into it, but if route level is the correct approach for your app, Apollo + GraphQL shouldn't be an exception.

Lastly, testing is so just so much easier if all your data-dependencies and async work are separated from components. Separation of concerns is a major productivity booster when you can test your components without having to worry about data-fetching. Of course in your tests you must populate your store by doing those async fetchings ahead of time, but with them separated from your components, you can easily formalize such work under some setup helper functions all your tests use to pre-populate the store before testing how components render :)

faceyspacey commented Sep 22, 2017

@raRaRa glad you like it! ...one of the biggest challenges is there hasn't been a good abstraction for route level data-fetching until now. React React v3 had callbacks, and the v4 has an excellent pattern (particularly with the react-router-config package), but it's non-obvious and not first-class in the v4. It's an after-thought. More importantly however, it doesn't live in Redux. So it's only really now for the first time that redux users have a complete "route level" abstraction.

I think now with RFR, we're gonna see a re-thinking of the importance of "route level" vs "component level." Things like Relay and Apollo have created a lot of hype around pairing data dependencies to components, but if you aren't using those 2, you're likely going to end up in a world of hurt at some point by doing things in lifecycle handlers.

That said eventually RFR will have a route level integration with Apollo. There's just very little benefits for doing things at the component level, minus the aforementioned rare exceptions. At least 80% of the time (and perhaps 100% in your app), you can determine all the data you need based on the route. If it's not possible, it's likely because you're deriving data in your "view layer" that very well could be re-thought to all be interpreted by the URL. So even with tools like Apollo you begin to engage in problematic anti-patterns that will likely eventually cause you pain by letting "state" live nowhere else but the view layer. I.e. the state necessary to perform those data-fetchings. You often have to refine the exact parameters used to fetch your data. So you take state and props and transform the 2 to get the params just prior to fetching the data. That's stuff that now lives in the view layer and subject to its re-rendering mechanisms (i.e. when lifecycle handlers are called). Every app at one point or another ends up with the data not fetched when it should be or fetched in response to unrelated props received/changed. And in all apps, you have to write extra code to guard against unrelated props that have changed. I.e. props that don't affect the params used to fetch data.

As for React Native + Apollo, there the problem is less because you don't have SSR and therefore potential for sequential data-fetchings happening on the server. However, do you really need component level data-fetching when screens are so small. You generally know what each "scene" needs in terms of its data. It's not like a desktop web panel dashboard with a million graphs, multiple tables, etc. That one use case is perhaps the biggest primary place where component level starts to make sense. But even there you can specify all your requirements for all those widgets in one place and fetch them via Apollo. I haven't given it enough thought yet, but the basic idea is to attach your GraphQL data-requirements to a route, and then in your components connect to the data just like any other Redux data. So more specifically, the idea is to eliminate having both react-redux's connect and Apollo's equivalent. I just want one, and we want it to be flatter. Using Apollo requires destructuring more deeply nested data to gain access to what you truly want in your components. The vision is one way that looks just like regular Redux, plus graphql specifications at the route level.

It's a bit premature to be talkin about it, as I have barely looked into it, but if route level is the correct approach for your app, Apollo + GraphQL shouldn't be an exception.

Lastly, testing is so just so much easier if all your data-dependencies and async work are separated from components. Separation of concerns is a major productivity booster when you can test your components without having to worry about data-fetching. Of course in your tests you must populate your store by doing those async fetchings ahead of time, but with them separated from your components, you can easily formalize such work under some setup helper functions all your tests use to pre-populate the store before testing how components render :)

@raRaRa

This comment has been minimized.

Show comment
Hide comment
@raRaRa

raRaRa Sep 22, 2017

@faceyspacey I completely agree. But I must admit that I haven't tried out Apollo nor GraphQL, I will definitely check them out and see how they fit my use cases.

I'm very used to writing web applications using the MVC pattern, where I basically pull all the data and dependencies on the controller. Then I inject the data to the views or the business logic. Like you said, that makes testing much easier since the view isn't coupled with the data fetching.

I've seen MVC applications where the View part is fetching data and doing business logic, and it's terrible. Both I have no clue what data will be fetched because it's hidden in some view/component and testing becomes harder/impossible.

To me, Redux-First Router is very much like the Controller part in MVC, plus the routes. Which makes so much sense :)

Thank you.

raRaRa commented Sep 22, 2017

@faceyspacey I completely agree. But I must admit that I haven't tried out Apollo nor GraphQL, I will definitely check them out and see how they fit my use cases.

I'm very used to writing web applications using the MVC pattern, where I basically pull all the data and dependencies on the controller. Then I inject the data to the views or the business logic. Like you said, that makes testing much easier since the view isn't coupled with the data fetching.

I've seen MVC applications where the View part is fetching data and doing business logic, and it's terrible. Both I have no clue what data will be fetched because it's hidden in some view/component and testing becomes harder/impossible.

To me, Redux-First Router is very much like the Controller part in MVC, plus the routes. Which makes so much sense :)

Thank you.

@faceyspacey

This comment has been minimized.

Show comment
Hide comment
@faceyspacey

faceyspacey Sep 22, 2017

At the end of the day, our new stack is this:

M: Redux
V: React
C: Redux-First Router (soon to be renamed to "Rudy")

When React came out (and particularly Redux), we all seemed to want to throw out the ancient wisdom of MVC, as if somehow it meant we could completely clear ourselves of the pains in developing previous applications. But I think ultimately it's nothing more than simply not having the tools built yet to support it. At the end of the day we still benefit greatly from MVC, even in reactive client-first applications.

It may work a bit differently in the new client-first stack vs. traditional server-side MVCs, but basically it's the same 3 separation of concerns.

As far as what the difference in interaction/behavior between the 3 is, it's basically that in the past the controller would get models in an initial phase and then render the views with them; and now it's the controller (RFR) immediately rendering a view (chosen via a "UI model," i.e. Redux state), and then re-rendering the view a second time once the controller finds domain models (also stored in Redux state).

faceyspacey commented Sep 22, 2017

At the end of the day, our new stack is this:

M: Redux
V: React
C: Redux-First Router (soon to be renamed to "Rudy")

When React came out (and particularly Redux), we all seemed to want to throw out the ancient wisdom of MVC, as if somehow it meant we could completely clear ourselves of the pains in developing previous applications. But I think ultimately it's nothing more than simply not having the tools built yet to support it. At the end of the day we still benefit greatly from MVC, even in reactive client-first applications.

It may work a bit differently in the new client-first stack vs. traditional server-side MVCs, but basically it's the same 3 separation of concerns.

As far as what the difference in interaction/behavior between the 3 is, it's basically that in the past the controller would get models in an initial phase and then render the views with them; and now it's the controller (RFR) immediately rendering a view (chosen via a "UI model," i.e. Redux state), and then re-rendering the view a second time once the controller finds domain models (also stored in Redux state).

@harut55555

This comment has been minimized.

Show comment
Hide comment
@harut55555

harut55555 Oct 9, 2017

Hello guys, watch this link. here is data server side rendering example https://github.com/bananaoomarang/isomorphic-redux

harut55555 commented Oct 9, 2017

Hello guys, watch this link. here is data server side rendering example https://github.com/bananaoomarang/isomorphic-redux

@timurtu

This comment has been minimized.

Show comment
Hide comment
@timurtu

timurtu Oct 10, 2017

@harut55555 idk it may just be me but this seems like a lot of code to do a get request and a post request

timurtu commented Oct 10, 2017

@harut55555 idk it may just be me but this seems like a lot of code to do a get request and a post request

@Tom910

This comment has been minimized.

Show comment
Hide comment
@Tom910

Tom910 Dec 5, 2017

Contributor

Any changes? Released 16 react and most of solutions do not work

Contributor

Tom910 commented Dec 5, 2017

Any changes? Released 16 react and most of solutions do not work

@timurtu

This comment has been minimized.

Show comment
Hide comment
@timurtu

timurtu Dec 5, 2017

I think the current approach is use redux with tooling like redux observable or redux thunk

timurtu commented Dec 5, 2017

I think the current approach is use redux with tooling like redux observable or redux thunk

@Tom910

This comment has been minimized.

Show comment
Hide comment
@Tom910

Tom910 Dec 6, 2017

Contributor

My case of use

<App>
    <Page>
        <AsyncModule hre="different.com/Button.react.js" /> downloaded from external url on server or client
    </Page>
</App>

The problem is that I do not know before rendering the App what kind of components I will have

Contributor

Tom910 commented Dec 6, 2017

My case of use

<App>
    <Page>
        <AsyncModule hre="different.com/Button.react.js" /> downloaded from external url on server or client
    </Page>
</App>

The problem is that I do not know before rendering the App what kind of components I will have

@timurtu

This comment has been minimized.

Show comment
Hide comment
@timurtu

timurtu Dec 6, 2017

Why not download data/content instead of a component just curious?

timurtu commented Dec 6, 2017

Why not download data/content instead of a component just curious?

@Tom910

This comment has been minimized.

Show comment
Hide comment
@Tom910

Tom910 Dec 6, 2017

Contributor

When? I have dynamic pages and the component may or may not be

Contributor

Tom910 commented Dec 6, 2017

When? I have dynamic pages and the component may or may not be

@timurtu

This comment has been minimized.

Show comment
Hide comment
@timurtu

timurtu Dec 6, 2017

Sounds like you want conditional rendering https://reactjs.org/docs/conditional-rendering.html

timurtu commented Dec 6, 2017

Sounds like you want conditional rendering https://reactjs.org/docs/conditional-rendering.html

@max-mykhailenko

This comment has been minimized.

Show comment
Hide comment
@max-mykhailenko

max-mykhailenko Dec 7, 2017

I think that it's routing problem, react should only render page, not manage and request render data

max-mykhailenko commented Dec 7, 2017

I think that it's routing problem, react should only render page, not manage and request render data

@wmertens

This comment has been minimized.

Show comment
Hide comment
@wmertens

wmertens Feb 6, 2018

Contributor

Using @graphql from Apollo makes data loading super simple, and the React-Router v4 component-based API makes for simple composable routing. Both are orthogonal to application state in Redux.

In order to do single-pass streaming rendering which waits for data fetches, it should be possible for render() to return a promise (on the client you just do as now, only on the server you return a promise).

Then @graphql can simply return a promise for the render() after data being loaded; all the rest is just plain React.

Contributor

wmertens commented Feb 6, 2018

Using @graphql from Apollo makes data loading super simple, and the React-Router v4 component-based API makes for simple composable routing. Both are orthogonal to application state in Redux.

In order to do single-pass streaming rendering which waits for data fetches, it should be possible for render() to return a promise (on the client you just do as now, only on the server you return a promise).

Then @graphql can simply return a promise for the render() after data being loaded; all the rest is just plain React.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon May 28, 2018

Member

A few months ago I gave a talk at JSConf Iceland that describes the upcoming async rendering features in React (see the second part): https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html. This is about client-side data fetching.

Now @acdlite gave a talk about how the same concepts can apply to enable asynchronously waiting for data on the server in React components, and progressively flushing down the markup as it’s ready: https://www.youtube.com/watch?v=z-6JC0_cOns

Hope you’ll enjoy watching these talks! I think that in a year or so we might be able to close out this issue and have an official strategy for this.

Member

gaearon commented May 28, 2018

A few months ago I gave a talk at JSConf Iceland that describes the upcoming async rendering features in React (see the second part): https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html. This is about client-side data fetching.

Now @acdlite gave a talk about how the same concepts can apply to enable asynchronously waiting for data on the server in React components, and progressively flushing down the markup as it’s ready: https://www.youtube.com/watch?v=z-6JC0_cOns

Hope you’ll enjoy watching these talks! I think that in a year or so we might be able to close out this issue and have an official strategy for this.

@ambernicole

This comment was marked as spam.

Show comment
Hide comment
@ambernicole

ambernicole Jun 24, 2018

I remember ring. Us getting married planning a future together. Please end this now and come see me. Time worked always love

ambernicole commented Jun 24, 2018

I remember ring. Us getting married planning a future together. Please end this now and come see me. Time worked always love

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment