ericclemmons / react-resolver Goto Github PK
View Code? Open in Web Editor NEWAsync rendering & data-fetching for universal React applications.
Home Page: https://ericclemmons.github.io/react-resolver
License: Other
Async rendering & data-fetching for universal React applications.
Home Page: https://ericclemmons.github.io/react-resolver
License: Other
link and linkName are just normal non async props. - is there/could there be a better way to pass them than..
export var YoAlt = Resolver.createContainer(InnerComponent, {
resolve: {
linkName:()=>"APPPPPP",
link:()=>'app1',
list: (props, context)=>
new Promise(
resolve=>
setTimeout(
(()=>
resolve(new List([{id:0,content:"ahhhh"}]))
),
1000)
)
}
});
for example maybe
export var YoAlt = Resolver.createContainer(InnerComponent, {
props:{
linkName:'APPPPP',
link:'app1'
},
resolve: {
list: (props, context)=>
new Promise(
resolve=>
setTimeout(
(()=>
resolve(new List([{id:0,content:"ahhhh"}]))
),
1000)
)
}
});
Anybody have ideas how to abort the promise from route A from resolving when I transition to route B while the promise hasn't been resolved yet?
To further illustrate this (I find my explanation a bit confusing), given that this is the normal flow:
transition to route A -> run route A promise -> [waiting.. do anything ] -> resolve
And if I try to go to route B, I expect:
transition to route A -> run route A promise -> go to route B -> abort route A promise -> run route B promise
What happens is:
transition to route A -> run route A promise -> go to route B -> [route A promise may resolve here] run route B promise -> [route A promise may resolve here] resolve route B promise
.
route A's promise should be aborted like Angular. Any thoughts in this? Thanks.
This should help with memory management & garbage collection.
any pointers as to how I could debug this?
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) <noscript data-reacti
(server) <div data-reactid=".1
On our internal project I saw:
Warning: owner-based and parent-based contexts differ (values:
[object Object]
vs[object Object]
) for key (parent) while mounting ResolverContainer (see: http://fb.me/react-context-by-parent)
This may be related to #27, or it may be some subtle difference.
(Although, there should be several updates in React v0.14 regarding context!)
Odd how this regression showed up:
Resolver.createContainer(Fixture, {
resolve: {
user: (props) => console.log(props),
}
});
// component, resolve
Whoops!
In an effort to reduce library size and dependencies lodash is a good candidate for removal.
You can get away with using ES5 for most of your lodash uses, for the rest perhaps you can include the lodash single function from npm.
My impression is that 'isomorphic' flux implementations tend to associate data fetching requirements with the router which imo is the wrong place - they should be associated with components - so I can change the set of components displayed on a route - or indeed in an app without routing and the data requirements change.
Whereas react-resolver is associating data requirements with the components. My question is (if this makes sense) can the two be combined to make a flux architecture which recursively loads data on the serer and client ar react-resolver does + data requirements are defined by components themselves. I think this is what https://www.npmjs.com/package/react-nexus might be but I didn't get it to work.
What I am thinking is that the promises configured by the resolve parameter could trigger actions and wait for store results something like:
Resolver.createContainer(InnerComponent, {
resolve:
{
data:async (props,context)=>{
await context.flux.getActions("data-action").goGetData(props.id);
return context.flux.getStore("data-store").getData();
}
}
}
I am also thinking you could have isomorphic forms...
Resolver.createContainer(InnerComponent, {
resolve:
{
data:async (props,context)=>{
await context.flux.getActions("data-action").goPostData(props.id);
return context.flux.getStore("data-store").getAcknowledgement();
}
}
}
Somehow, this is preferable:
@resolve
class SomeComponent extends React.Component {
static resolve = {
user() => {
}
};
}
I have an issue where the client side routes break after navigating to a single link. From then on the path changes in the address bar but nothing gets updated on the screen. All the Handlers are returning standard React components without wrapping with Resolvers.
package.json
"react": "0.13.3",
"react-router": "0.13.3",
"react-resolver": "^2.0.4",
"react-router-server-location": "^2.0.0",
Routes.js
<Route path="/" handler={Main}>
<DefaultRoute handler={Home} />
<Route name="signin" path="/signin" handler={Signin}/>
<Route name="signup" path="/signup" handler={Signup}/>
<Route name="forgot_password" path="/users/password" handler={ForgotPassword}/>
<Route name="reset_password" path="/users/password/edit" handler={ResetPassword}/>
<Route name="account" path="/account/profile" handler={AccountNavigation}>
<DefaultRoute name="account_profile" handler={AccountProfile} />
<Route name="account_settings" path="/account/settings" handler={AccountSettings} />
</Route>
</Route>
client.js
Router.run(routes, Router.HistoryLocation, (Root) => {
Resolver.render(() => <Root />, document.getElementById("react-root"));
});
This looks like a cleaner implementation:
I'm using React Router v1.0.3
.
I'm trying this kind of route hierarchy:
-/ (top-level)
--/ (home page)
--/about (about page)
--/friends (/friends route top-level, fetching of friends)
----/ (friends index; /friends)
----/list (friends list; /friends/list)
(to code)
<Route path="friends" component={Friends}>
<Route path="/" component={FriendsIndex} />
<Route path="list" component={FriendsList} />
</Route>
Where I am putting the Resolver container on the friends
top-level component (Friends
).
Here's the stack trace:
Uncaught (in promise) TypeError: Cannot read property 'ref' of undefined
at cloneWithProps (http://localhost:1111/bundle.js:20673:13)
at Friends.render (http://localhost:1111/bundle.js:23549:49)
at ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext (http://localhost:1111/bundle.js:10462:34)
at ReactCompositeComponentMixin._renderValidatedComponent (http://localhost:1111/bundle.js:10489:14)
at ReactPerf.measure.wrapper (http://localhost:1111/bundle.js:16948:21)
at ReactCompositeComponentMixin.mountComponent (http://localhost:1111/bundle.js:9910:30)
at ReactPerf.measure.wrapper [as mountComponent] (http://localhost:1111/bundle.js:16948:21)
at Object.ReactReconciler.mountComponent (http://localhost:1111/bundle.js:17763:35)
at ReactCompositeComponentMixin._updateRenderedComponent (http://localhost:1111/bundle.js:10437:40)
at ReactCompositeComponentMixin._performComponentUpdate (http://localhost:1111/bundle.js:10399:10)
Check this repo http://srph.github.io/rrt/#/friends (now https://github.com/srph/playground/tree/master/react/react-resolver-prop-bug)
Not sure what I'm doing wrong haha.
Edit (8/27/2014): Repository transferred to https://github.com/srph/playground/tree/master/react/react-resolver-prop-bug (link to demo)
I haven’t peaked behind the covers of react-native too much yet so I have no idea on the effort involved. Is this something you’d be interested in this project supporting?
Sorry to bother on this one;
I was interested in your tool, without being able to run the examples, stuck with the following error :
[piping] can't execute file: /Users/xx/Desktop/react-resolver/examples/react-v0.13/watch.js
[piping] error given was: Error: Cannot find module 'babel/register'
at Function.Module._resolveFilename (module.js:336:15)
at Function.module._load (/Users/xx/Desktop/react-resolver/examples/react-v0.13/node_modules/piping/lib/launcher.js:24:23)
at Module.require (module.js:365:17)
at require (module.js:384:17)
at Object.<anonymous> (/Users/xx/Desktop/react-resolver/examples/react-v0.13/src/server.js:1:63)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Module._load (module.js:310:12)
at Function.module._load (/Users/xx/Desktop/react-resolver/examples/react-v0.13/node_modules/piping/lib/launcher.js:32:16)
[piping] further repeats of this error will be suppressed...
I wonder if that would be an easy answer;
thanks for your time
I've been noticing an issue where the componentWillMount/render methods inside my root component get called twice when rendering on the server. Attached below are some code snippets from the important parts.
Routes.js
<Route path="/" handler={Main}>
<DefaultRoute handler={Home} />
<Route name="signin" path="/signin" handler={Signin}/>
<Route name="signup" path="/signup" handler={Signup}/>
<Route name="forgot_password" path="/users/password" handler={ForgotPassword}/>
<Route name="reset_password" path="/users/password/edit" handler={ResetPassword}/>
<Route name="account" path="/account/profile" handler={AccountNavigation}>
<DefaultRoute name="account_profile" handler={AccountProfile} />
<Route name="account_settings" path="/account/settings" handler={AccountSettings} />
</Route>
</Route>
server.js
const location = new ServerLocation({ request, reply });
Router.create({ location, routes }).run(function(Handler, state) {
Resolver.resolve(() => <Handler {...state} />).then(({Resolved, data}) => {
reply(`
<!DOCTYPE html>
<html lang="en-us">
<head>
...
</head>
<body>
<div id="react-root">${React.renderToString(<Resolved />)}</div>
<script src="${webserver}/dist/client.js" async defer></script>
</body>
</html>
`);
}).catch((error) => reply(error.stack).type("text/plain").code(500)); // Just in case!
});
Main.js
...
componentWillMount () {
if (__SERVER__) {
console.log("Hello server");
}
if (__CLIENT__) {
console.log("Hello client");
}
}
...
logs
[1] ==> ✅ Server is listening
[1] ==> 🌎 Go to http://0.0.0.0:8000
[1] onPreResponse
[1] Hello server
[1] Hello server
Is anyone else seeing this issue? The issue goes away when I remove the react-resolver from the equation.
resolver.states
for serialization and output on the server.new Resolver(window.__resolver__)
or something .I believe there is value in keeping bluebird, however if you're running in an ES6-shimmed environment then you already having promises support making bluebird redundant. It would be great to provide a build that has no dependencies this way you get a minimal react-resolver.
Decorators do decorate(options)(Component)
, not decorate(Component)(options)
.
Whoops!
Everything looks fine to me
// app.js
import {Resolver} from "react-resolver";
window.router = ReactRouter.create({
routes: routes,
location: HistoryLocation
});
window.router.run((Handler, state) => {
Resolver.render(<Handler/>, document.getElementById("main")));
});
// home.js
class Home extends React.Component {
static displayName = "Home"
render() {
return <div>Home</div>;
}
}
export default Resolver.createContainer(Home, {
resolve: {}
});
Getting Resolver is not defined in either context
or props
. (Perhaps missing a root <Container resolver={new Resolver()} />?)
React: 0.13.1
React-Resolver: 0.1.1
Using v1:
Also, this should be a form example, IMO. Something to warrant the router, but also having a store.
@goatslacker Didn't you stop using Symbol
in Alt? What was the reason behind that again?
It works pretty well with anything using the Babel Polyfill, but with ES5 users in mind, should I just drop it & use local constants instead?
Hey, there, I feel like I must be missing something obvious, but I'm not sure where. Why isn't the resolving running when I transition into a sibling state? How should this be done correctly?
Created a repo to demonstrate:
I'm in bed right now, but the gist is that I need to ensure nested resolvers (like those in the form project) get re-resolved when the parent does.
This seems pretty cool, but I am still a little unclear as to how to best utilize this.
If I have route that is something like:
<Route name='user' path='/user/:username/?' handler={UserView}>
<DefaultRoute handler={UserAboutSubview} />
<Route name='user-activity' path='activity/?' handler={UserActivitySubview} />
<Route name='user-favorites' path='favs/?' handler={UserFavsSubview} />
</Route>
Normally, I would think:
UserView
, calls an action
to initialize the UsersStore
, on componentDidMount
, and then setState
upon store update
. Then pass the state
as props
to UserAboutSubview
, and then listen to any changes on UsersStore
.But if you use react-resolver
, would you would still initialize the the UsersStore
, at UserView
, and then instead of passing down props, the Subviews
, fetch the data from the store?
How would updates from the UsersStore
be propagated?
(So that other users can take advantage of this)
Boolean(loaded)
Array(promises)
@loading(MyLoader)
Promises are nice but I like flexibility, can we have a react-resolver core and then we choose whether we want promises, callbacks, generators, thunks, or whatever emerges next.
From the usage description in the readme it seems like there is no mixin needed, but the examples use the Resolver.mixin
. In my tests the resolve
callbacks are not called without the mixin.
Is there a way to use react-resolver without any mixins? If not it would be nice to mention that in the readme.
Currently the Resolver tests that a @resolve
Promise is instanceof Promise
. Promises seem to be a bit fickle in that there are many different implementations and ways to get a global Promise object.
A few possibilities:
The question is should we expect one global Promise object or provide a duckpunch to interop with the different methods?
Alternatively, we could ignore Promises and use a callback which the consumer is responsible for calling in whatever async control flow implementation they should choose.
Hey awesome to be collaborating. react-resolver and solve have a different syntax for callbacks, so we should consider which syntax to use.
solve doesn't use node-style callbacks (function(error, data)). My thinking here is that if data is being resolved as an object or arrays etc., then only a single value can be emitted from an async resolution, so the callback only takes a single argument. So error handling has to be somehow resolved into the props object.
One option might be to have multiple types on a single data point, eg.
{
value: function(done) {
request.get('/api/data', function(error, result) {
if (error) {
return done(new Error("Failed to load"));
}
return done(result);
});
}
}
// later in render
if (this.props.value instanceof Error) {
return <Error error={this.props.value} />;
}
// otherwise
return <Component value={this.props.value} />;
another option might be to resolve to multiple properties of an object, eg.
{
value: function(done) {
request.get('/api/data', function(error, result) {
if (error) {
return done({ error: new Error("Failed to load"), data: null });
}
return done({ error: null, data: result });
});
}
}
// later in render
if (this.props.value.error !== null) {
return <Error error={this.props.value.error} />;
}
// otherwise
return <Component value={this.props.value.data} />;
Thoughts on this? Do you think the node/async (error, data) callback syntax is important to maintain? Or do you agree that it make sense to force the user to think about how to translate the errors into a props object in a more deliberate way?
This should resolve the issue with react-router
being part of package.json
.
Also, it would be best if the examples/
had their own package.json
.
Pretty neat.
@resolve
class SomeComponent extends React.Component {
static resolve = {
user() => {
}
};
}
$ npm install --save react-resolver@latest
This will avoid a stupid bug with v1.1.7.
Lets take the stargazer example. Say I have stargazers and a count, I want to resolve the same data for both of them. There are two ways I can go about this:
Here's an example:
class App extends React.Component {
render() {
return (
<div>
<Count />
<Stargazer />
</div>
)
}
}
@resolve({
props: {
users(props) {
const url = `https://api.github.com/repos/${props.user}/${props.repo}/stargazers`;
return axios.get(url).then(response => response.data);
},
},
})
class Stargazer extends React.Component {
render() {
return (
<div>{this.props.users.map(user => <User user={user} />)}</div>
)
}
}
@resolve({
props: {
users(props) {
const url = `https://api.github.com/repos/${props.user}/${props.repo}/stargazers`;
return axios.get(url).then(response => response.data);
},
},
})
class Count extends React.Component {
render() {
return (
<div>The number of people are: {this.props.user.length}</div>
)
}
}
This would fire off two remote requests. Ideally this would only fire a single request and the response would be handled by both resolvers.
Though this project (which started internally @ work) was made public a couple months before @RickWong's react-transmit
's initial commit, Transmit's comparison to Relay, React Native support, and continued development looks like it may have superseded this project.
Our goals are similar, although with v1
I dropped the comparisons to Relay, since lack of GraphQL made the comparison to Relay tenuous at best, in my opinion. Really, this project's goals were always to support isomorphic lazy-loading of data, primarily for work.
I've continued development of this project internally, since a major relaunch @ work greatly depends on this (there's even a sibling Flux implementation that gets around dispatcher issues), but I'm curious if it's best to let @RickWong's project be endorsed rather than risk fragmentation, stagnation of this project, or competing over the same problems.
I've tagged @RickWong several times, mainly to get his feedback :)
The README file in the Stargazers example project still references the old folder name (examples/contacts) and includes instructions for npm link
that were no longer needed since the addition of the pre/postinstall copying.
I also found that an initial npm install
in the stargazers directory produces an error, since the preinstall script expects the node_modules
and node_modules/react-resolver
folders to already exist. That's not a problem for subsequent installs, but requires intervention to work on a freshly cloned repo.
/examples/stargazers$ npm install
> @ preinstall ~/resolver-dev/examples/stargazers
> rm -rf node_modules/react-resolver && cp -R ../../dist node_modules/react-resolver
cp: node_modules/react-resolver: No such file or directory
cp: ../../dist: unable to copy extended attributes to node_modules/react-resolver: No such file or directory
cp: node_modules/react-resolver/Container.js: No such file or directory
cp: node_modules/react-resolver/index.js: No such file or directory
cp: node_modules/react-resolver/Resolver.js: No such file or directory
cp: node_modules/react-resolver/ResolverError.js: No such file or directory
npm ERR! @ preinstall: `rm -rf node_modules/react-resolver && cp -R ../../dist node_modules/react-resolver`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ preinstall script.
npm ERR! This is most likely a problem with the package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! rm -rf node_modules/react-resolver && cp -R ../../dist node_modules/react-resolver
npm ERR! You can get their info via:
npm ERR! npm owner ls
npm ERR! There is likely additional logging output above.
npm ERR! System Darwin 13.4.0
npm ERR! command "~/.nvm/v0.10.38/bin/node" "/Users/jason/.nvm/v0.10.38/bin/npm" "install"
npm ERR! cwd ~/resolver-dev/examples/stargazers
npm ERR! node -v v0.10.38
npm ERR! npm -v 1.4.28
npm ERR! code ELIFECYCLE
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR! ~/resolver-dev/examples/stargazers/npm-debug.log
npm ERR! not ok code 0
So in addition to the README cleanup, the post- and preinstall need a minor adjustment to run without error on first attempt.
Question -- does this has to be done manually?
Because Resolver.render
has an hard dependency on React.render
it cannot be used with (for example) react-titanium
or react-blessed
.
An initial thought is to transform
class Resolver {
static render(element, node, instance = new Resolver()) {
React.render((
<Container resolver={instance}>
{element}
</Container>
), node);
return instance;
}
}
to
class Resolver {
static wrap(element, instance = new Resolver()) {
return (
<Container resolver={instance}>
{element}
</Container>
);
}
static render(element, node, instance = new Resolver()) {
React.render(Resolver.wrap(element, instance), node);
return instance;
}
}
so that I can use it with my renderer, such as
import { render } from 'react-titanium';
import { wrap } from 'react-resolver';
class App extends Component { /*...*/ }
render(wrap(<App />));
I'd love to know when all props have resolved or rejected so I can hook into this and add some nice alt support for caching resolver data.
Example API:
@resolve({
props: {
users(props) {
const url = `https://api.github.com/repos/${props.user}/${props.repo}/stargazers`;
return axios.get(url).then(response => response.data);
},
},
onResolved(props) {
AltActions.resolved(props);
},
onRejected(props) {
AltActions.failed(props);
}
}
You know what, I'm an opinionated guy, so here comes an opinion!
Sharing based off of key (e.g. user
) is not smart by default, because there's no guarantee both components are referencing the same thing.
var JoeComponent = React.createClass({
statics: {
resolve: {
user: function() {
return UserService.findByName('Joe');
}
}
}
...
});
var MoeComponent = React.createClass({
statics: {
resolve: {
user: function() {
return UserService.findByName('Moe');
}
}
}
...
});
Perhaps the user can explicitly opt-in to certain keys to avoid the mess that is context
...
Suppose a top level AppHandler
can share the following:
flux: function() {
return new Alt();
},
propTypes: {
flux: React.PropTypes.object.isRequired
}
Then any any component that wants to utilize this can use a custom PropType
:
http://facebook.github.io/react/docs/reusable-components.html
propTypes: {
flux: Resolver.PropTypes.shared.isRequired
}
This feels very similar to context
, now that I write it.... :)
Both examples is missing a client.min.js
build and express handling of requests to this file.
... or am I missing somthing?
Module not found: Cannot resolve 'file' or 'directory' ./Resolver in <path/to/dist>
I just switched to Ubuntu Server 14.04. I just checked /dist/
, and it doesn't seem to have the Resolver.js
file, but only resolver.js
This would technically be a BC break, but I encountered a situation today where "hiding" props didn't help.
Take Stargazers.js
, for example:
react-resolver/examples/stargazers/components/Stargazers.js
Lines 68 to 80 in ca5a8c9
<Stargazers />
requires a users
prop, but the <StargazersContainer />
requires user
and repo
to fetch the data for users
.
As a result, <Stargazers />
does not have access to user
or repo
.
This gets slightly annoying when composing multiple containers, as you pass parent props down the chain:
resolve: {
something: (props) => props.something
}
My gut says that this mild inconvenience can be solved through another means, and that the base component shouldn't expect parent props to be leaked through...
Since react-resolver uses promise wrappers around callbacks, it may have a problem with wiring up with Flux stores that have callbacks that could be called multiple times with data.
I have a Flux library Hoverboard that works with subscribing to a callback function. As changes come through the store via user actions, the data updates asynchronously. This works well with solve (I built them to work together in a Flux framework).
Put more simply, react-resolver may need to be modified to work with the following code:
statics: {
resolve: {
count: function(done) {
var count = 1;
setInterval(function(){
done(count++);
}, 1000);
}
}
}
Thoughts?
I'm running into a problem with server rendering that may simply reflect a misunderstanding of the server capabilities of Resolver.renderToString
.
My project uses react-router
and has nested Resolver containers, so that /category/[x] might use a container to fetch a list of items under category x, and then /category/[x]/item/[y] would fetch a particular item in full detail, and so on with further nesting. All of this runs smoothly on the client.
When server rendering with Resolver.renderToString
, it appears only to work through one level of Resolver containers; subsequent containers that create promises down the tree after the first one resolves will end up giving me a variant on this error:
Resolver is frozen for server rendering.
ResolverContainer (#.0.0.0) should have already resolved "item".
Is there a strategy that can be used for this scenario, forcing Resolver to wait for all promises and resolutions down the tree before freezing itself? Or should I be approaching it from a different angle?
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.