ryanflorence / async-props Goto Github PK
View Code? Open in Web Editor NEWCo-located data loading for React Router
License: MIT License
Co-located data loading for React Router
License: MIT License
Hello, I noticed that the project has not been very active during the last year. Is it still relevant or did I miss a new and more main stream way to handle the the same purpose?
When I have
static loadProps(params, cb) {
cb(null, { test: 'hi'})
}
inside a component, linking to that page does not work. The moment I remove the loadProps property and reload the page, using <Link />
to that page works again. The url updates but the page does not actually switch unless you do a refresh.
Edit: Also noticed this only happens when loadProps is inside of a child route component.
Hi.
I tried to show loading message through renderLoading
the callback never triggered.
Hope to pre-release for [email protected] or hot-fix it.
Thank you!
Edit
renderLoading
is only used in first-loading in client-side. I got it incorrectly. Hope to add callback for every loading.
Hey, sometimes I need to set the page title based on something returned from an api. For now I've been doing something like this inside of loadPropsOnServer
if(asyncProps.propsArray[asyncProps.propsArray.length - 1].title) {
title = asyncProps.propsArray[asyncProps.propsArray.length - 1].title
}
I'm just curious if @ryanflorence would like to add a better way to do this or if that is outside of the scope of this package.
Nice library, it will solve many problems!
So, attempting to render a string "<script>"
block in a react component used for server side rendering is way more ugly than it should be, needing to completely take control of a containing element.
So sure, no problem yeh, we can:
<script
dangerouslySetInnerHTML={{
__html: `window.__ASYNC_PROPS__ = '${JSON.stringify(asyncProps.propsArray)}';`,
}}
/>
But, why have the script string... an object is far more composable, it's a primitive structure. The script string seems to just overcomplicate things?
Why the generated script string?
Also, why should __ASYNC_PROPS__
contain a serialised JSON instead of just a normal JS object?
I discovered that the static loadProps
method has to be defined in all Components, otherwise the async-props will not receive an callback and update - easy enough.
So i defined empty callbacks for testing purposes
statics: {
loadProps: function(params, cb) {
cb(null);
}
}
funny thing: the { loading } = this.props
in the root component does get updated correctly but the { this.props.children }
does not recognize any changes...
so i had wo hack the system a bit and call it with an zero timeout and whoop - it works:
statics: {
loadProps: function(params, cb) {
setTimeout(function() {
cb(null);
},0);
}
}
don't know it is an bug - or an feature :)
Hi.
loadProps is executed twice and loading time is also double.
Is there a reason? Or is it just a bug? It is in current master (not released, for [email protected])
Thank you!
The current implementation only supports rendering after loadProps
invokes a callback. Wouldn't it be an interesting addition to also support loadProps
returning a Promise and render after the promises resolve?
invariant.js:39 Uncaught Error: Invariant Violation: You're trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser quirks by rendering at the document root. You should look for environment dependent code in your components and ensure the props are the same client and server side:
(client) <html><head><noscript dat
(server) <html data-reactid=".1xl9
My client code:
render(<Router history={history} RoutingContext={AsyncProps}>{routes}</Router>, window.document);
Old:
render(<Router history={history}>{routes}</Router>, window.document);
Server:
res.status(200).send(renderToString(<AsyncProps {...renderProps} {...asyncProps} />));
Old:
res.status(200).send(renderToString(<RoutingContext {...renderProps} />));
If I return the old client code (remove 'RoutingContext={AsyncProps}') no error.
When no components define static loadProps, loadPropsOnServer never fires it's callback.
I am trying async-props with my project
app.js
var React = require('react');
var ReactDOM = require('react-dom');
var Router = require('react-router').Router;
var browserHistory = require('react-router').browserHistory;
var AsyncProps = require('async-props');
var rootRoute = {
component: 'div',
childRoutes: [{
path: '/',
component: require('./components/App.js'),
indexRoute: {
component: require('./components/Home.js')
},
childRoutes: [
require('./routes/Page.js'),
{
path: '*',
component: require('./components/NotFound.js')
}
]
}]
};
ReactDOM.render(
<Router history={browserHistory} routes={rootRoute} render={(props) => (
<AsyncProps {...props} renderLoading={() => <div>Loading...</div>}/>
)}/>,
document.getElementById('app')
);
if I remove render prop in Router, the error will gone:
ReactDOM.render(
<Router history={browserHistory} routes={rootRoute}/>,
document.getElementById('app')
);
package.json
...
"async-props": "^0.3.2",
"react": "^15.1.0",
"react-dom": "^15.1.0",
"react-router": "^2.4.1",
...
This is just a question. I'm trying to introduce async-props into my package.json. I've babel as a dependency but async-props post install was failing when doing npm install
.
Only after install babel globally the install worked. What I'm missing. It's necessary post install script?
If you wanted to have a "something is loading" throbber at the top level of your app to indicate loading with async-props is currently happening somewhere in the route hierarchy, is there any way async-props can provide that information, or would you have to manage it externally via your static loadProps
hooks?
I've made a fork where I was trying to build an async-props example with RR2, using its latest from master, but noticed that dynamic segments are not working correctly. The address bar will update but the next view will not load. Hitting the back button at that point will cause it to render the view that previously would have rendered.
Here's my example: https://github.com/bdefore/react-router/blob/async-props-example/examples/async-props/app.js
The only difference between it working correctly and causing this behavior is in removing AsyncProps from render
:
<Router render={(props) => <AsyncProps {...props} />} history={browserHistory}>
vs:
<Router history={browserHistory}>
Nice async-props and example !
I try to use falcor to build async model(JSON Graph: remote data model, ui model). And I use async-props to fetch all the state(falcor model get) in route component.
When dispatch a action(falcor model set/call), the server will return updated JSON Graph. The app will reload async props of all route components, then render.
example: https://github.com/ustccjw/tech-blog
I feel like the readme of this repo needs an explanation for the motivation behind this library.
As far as I can see, the main purpose is providing a known interface that a server can use to know when a route has all of its asynchronous resources loaded that are required for a full render. I hope I'm correct here, but I only discovered this after reading into server side react for the first time.
Howdy!
I'm curious if you'd be open to allowing for a new API (via props on AsyncProps) that would allow individual components to handle their own loading state, rather than AsyncProps only rendering the new Component once its loadProps
method calls the callback?
Right now I can work around this via:
static loadProps({ params }, cb) {
if (__CLIENT__) { // Flag injected by webpack
cb();
}
api.makeRequest().then(cb);
}
However I would like to make this the default behavior.
Thoughts?
Should this be available in loadProps
as a parameter to access things like query params? Currently I don't see how to do so.
ping @ryanflorence
https://github.com/rackt/react-router/blob/master/examples/huge-apps/routes/Profile/index.js
module.exports = {
path: 'profile',
getComponent(location, cb) {
require.ensure([], (require) => {
cb(null, require('./components/Profile'))
})
}
}
AsyncProps will work with Profile
component ?
I tried following the server example in README.md but got the error cb is not a function
. After looking at loadPropsOnServer it looks like it's expecting 3 arguments where the last is the callback whereas the example has 2 unless I'm missing something?
What should be passed as the second argument, loadContext?
Other than that this library solves a much needed problem so thanks very much :)
I could be wrong here, but from testing, there appears to be a bug in how the static method loadProps
is called.
For example, the method won't be called (shown in code below):
export default ({ children }) => (
children || <DefaultPage />
);
class DefaultPage extends Component {
constructor(props) {
super(props);
}
static loadProps(stuff, cb) {
console.log('async props called');
cb(null, {});
}
render() {
return (
<div className="pw__wrapper">
</div>
);
}
}
But in the example below, the method will be called
export default class DefaultPage extends Component {
constructor(props) {
super(props);
}
static loadProps(stuff, cb) {
console.log('async props called');
cb(null, {});
}
render() {
return (
<div className="pw__wrapper">
</div>
);
}
}
Not sure if this intended or not?
I know it's been discussed in #4 but I want to bring it up again here.
The way the script thing works now, the user-land code gets handed a string of the form:
"<script>__ASYNC_PROPS__ = /* .... */ </script>"
This is quiet handy in the case you use js template strings to insert the script into your html (as shown in the README):
html = (markup, script) => `
<html>
<!-- ... -->
${markup}
${script}
</html>
`
I for one, do not use js string templates to generate my html, I use react components. To get the script working I now need to do either:
<span dangerouslySetInnerHTML={{__html: script }} />
which adds a superflous <span>
. Or I can make my markup a bit cleaner with:
const data = script.replace(/^<script>/, '').replace(/<\/script>$/, '');
//...
<script dangerouslySetInnerHTML={{__html: data }} />
Both of these solutions seem a bit hacky. I prefer to keep dangerouslySetInnerHTML
out of my code
completely. I also would like to be able to minify the script code when in production.
How can we fix this? It seems like there are a lot of people who are fine with the way things are now, but
we should be able to expand the use-case to other users.
I propose the following: make the script
parameter be an object that knows how to transform the data to any of the preferred forms (the string with script tags, the pure object, ....) so people can choose which they like. So people can call script.asString()
or script.data()
to be able to use their use case.
Just adding a note here so we don't forget.
HI ๐
I'm testing master branch of this on my private universal app,
and encountered a problem on SSR.
// client
render(
<Router
history={this.history}
render={props => (
<AsyncProps {...props} />
)}>
{this.routes}
</Router>
);
// server
// .. in `loadPropsOnServer`'s cb
renderToString(
<AsyncProps {...renderProps} {...asyncProps} />
);
It works, but checksum was invalid
warning occurs like this:
Warning: React attempted to reuse markup in a container but the checksum was invalid.
...
(client) <noscript data-reacti
(server) <div data-reactid=".y`
I've tried <Router render={props => <AsyncProps {...props} />} ...
instead of just <AsyncProps />
on server side too, but it doesn't work.
fetching and bind scriptString is fine, but App's react components render on client side, after intial server rendered.
am I missed something?
thanks
Here's an example where I'm going up the route tree from a /:user/:repo
to a different /:user
where there are User and Repo route components - it never gets to compare the user
props against eachother so the user data doesn't get reloaded:
Seems like it should check the components one at time for equality them param changes inside this block.
Edit: live, heavy meta, example: https://insin.github.io/react-nwb-github-issues/#/rackt/async-props/9
I want to access my flux instance in loadProps
. On the server side I can manipulate params
to include my flux instance, but it feels a bit hacky, but on the client-side the invocation of loadProps
is hidden by RoutingContext={AsyncProps}
. What would be the best way to access a request-specific variable in loadProps
?
Hi. I got an error in client-side:
client-side react render code:
<Router history={history} createElement={AsyncProps}>
<Route path="/" component={App}>
<Route path="*" component={ContentPage} />
<IndexRoute component={HomePage} />
</Route>
</Router>
RoutingContext attribute did not work on [email protected], I tried createElement
attributes as similar. Is there plan to fix it? or should I downgrade react-router?
renderProps
give 'undefined' components. so when check loadProps, there's need to filter undefined components as:const components = renderProps.components.filter(x => !!x);
renderProps.components = components;
Thank you!
I just tried to use async-props with react-router, and createBrowserHistory
and everything works fine apart from one thing:
Here's a short form of my code:
(CJSX) (React Router: 1.0.3, Async-props: 0.2.2 )
My routing file:
ReactDom.render(
<Router RoutingContext={AsyncProps} history={ createBrowserHistory() }>
{ routes }
</Router>
,document.getElementById("app"))
And the components both look something like this.
class Home extends React.Component
@contextTypes:
history: require("react-router").PropTypes.history
displayName: "Home"
I can call @context.history.push
etc. and it navigates properly, but if you use the back button in the browser nothing happens. I tracked it down to the RoutingContext
. When I remove it everything works.
(Navigation using <Link>
also works, but the back button is broken too)
I hope my explanation didn't sound too crazy and you somewhat understand my problem.. Thanks in advance!
Since the HoC or AltContainer wraps your component and makes it impossible to call any static method on your component you have to use a util from alt called statics
import connectToStores from 'alt-utils/lib/connectToStores';
import statics from 'alt-utils/lib/statics';
class SomeComponent extends React.Component{
static getStores() {
return [SomeStore]
}
static getPropsFromStores() {
return SomeStore.getState()
}
render(){
return ....
}
}
//declare your functions as constants
const loadProps = (params, cb) => {
cb(null, {
tacos: [ 'Pollo', 'Carnitas' ]
})
}
//pass them to statics first param (used ES6 desctructor)
export default statics(
{ loadAsyncData },
connectToStores(WorkflowPage)
);
To reproduce:
static loadProps(params, cb) {
setTimeout(() => cb(null, {result: true}), 3000)
}
Render a page with <Link />
and click on it within the first 3 seconds. The url will update, but the content doesn't, because AsyncProps breaks. Trying to figure out a solution.
Hi.
To enable display common loading animation, loading
prop can help it shows.
If root component reach loading
prop, it would be useful.
Thank you
It looks to me as if createElement
is overridden. What if I wanted to pass down some extra props to all components on the client side?
Is there a way of having more than one createElement
? Or can using it be avoided?
npm doesn't have the latest changes on master.
Hey, Iโd like to use async-props
in my current project, but it lacks Promise support. How about mixing new and old school, huh?
If you like the idea, Iโm willing to implement this feature myself.
It feels weird doing loadContext.user
wouldn't it make more sense if it was just context.user
?
I notice that if I have error code in getInitialState
or render
method and so on,
the console never shows my error, it's hard to debug,
error would only show in console when user interact with the component and some code is wrong
Hey,
I've implemented this approach into my boilerplate (isomorphic with dynamic component loading) for displaying a Progress Bar when a dynamic route is loading.
I got a very similar setup to the example 'auth-with-shared-root' provided in the react-router examples https://github.com/rackt/react-router/blob/master/examples/auth-with-shared-root/config/routes.js
Some childRoutes got an onEnter
middleware.
{ onEnter: redirectToLogin,
childRoutes: [
// Protected routes that don't share the dashboard UI
{ path: '/user/:id',
getComponent: (location, cb) => {
require.ensure([], (require) => {
cb(null, require('../components/User'))
})
}
}
// ...
]
},
Any of these endpoints get the error Uncaught TypeError: Cannot read property 'loadProps' of undefined when routing to them. But when i remove the onEnter
method and require.ensure it directly thise work fine.
Do i have to pass different attributes when using onEnter??
I have a use case where I need to get the request
object inside of loadProps if I am on the server. Is there any way to pass down something through the AsyncProps component? How do you propose adding this?
I am doing renderProps.params.token = req.session.token
above loadPropsOnServer
for now.
Hi there,
I noticed that callback passed to loadAsyncProps never fails:
https://github.com/rackt/async-props/blob/master/modules/AsyncProps.js#L40
so it looks like handleError doesn't make sense
https://github.com/rackt/async-props/blob/master/modules/AsyncProps.js#L265
Did I miss something?
First, thank you for releasing this library. I'm still fairly new to React and have been trying to learn it without jumping to the complexity of something like redux before it is necessary. This library fits very well in the space where I thought I would need to build something custom for myself.
With the goal of trying to keep complexity low, I've started with a setup that is a bit different than what is suggested here. For my server-side rendering, I am not using async-props, but am passing the initial props through a custom createElement
function. The need for this custom function for such simple use cases still seems unnecessary, but thats a different topic.
Because of this approach, I have not differentiated between which props are needed for each of the routable components for that page load, but rather just have a flat object with all of my data for that page. Therefore, I have to jump through a few extra hoops and leverage some knowledge about which components should be rendered, which doesn't feel like it should be the responsibility for this layer of my app.
It sounds like the current state of this project is still a pretty early design, so I'm hoping that things could still be flexible enough to support this type of use case. Would it be reasonable to support a flattened object literal for __ASYNC_PROPS__
, either instead of the current list of objects, or at least optionally? This would enable the flexibility to use async-props client-side with an alternative server-side rendering technique without the requirement to jump through hoops to match the expected list.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.