Code Monkey home page Code Monkey logo

react-async's Introduction

React Async

VERSION DESCRIBED IN THIS DOCUMENT IS NOT RELEASED YET

React Async provides a way for React components to subscribe for observable values.

Installation

React Async is packaged on npm:

$ npm install react-async

Basic usage

React Async provides a component decorator @Async which given a set of observable specifications wraps a regular React component and returns a new one which subscribes to observables and re-renders the component when new data arrives.

The basic example looks like:

import React from 'react';
import Async from 'react-async';
import Rx from 'rx';

function defineXHRObservable(url) {
  return {
    id: url,
    start() {
      return Rx.fromPromise(fetch(url))
    }
  }
}

function MyComponentObservables(props) {
  return {
    user: defineXHRObservable(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentObservables)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }

}

The @Async decorator injects data from observables via props so in render() method of <MyComponent /> the user property will contain the data fetched via XHR.

Rendering async components on server with fetched async state

While React provides renderToString(element) function which can produce markup for a component, this function is synchronous. That means that it can't be used when you want to get markup from server populated with data.

React Async provides another version of renderToString(element) which is asynchronous and fetches all data defined in observable specifications before rendering a passed component tree.

First, you'd need to install fibers package from npm to use that function:

$ npm install fibers

Then use it like:

import {renderToString} from 'react-async';

renderToString(
  <Component />,
  function(err, markup) {
    // send markup to browser
  })

This way allows you to have asynchronous components arbitrary deep in the hierarchy.

react-async's People

Contributors

andreypopp avatar berekuk avatar eliseumds avatar matiassingers avatar matthewwithanm avatar mekwall avatar ryankask avatar strml avatar threeaims avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-async's Issues

Thoughts on Relay?

Hi, do you have any thoughts on Facebook's Relay?
It turns out react-async (+ the router) is more on par for a better model than flux implementations.

Serialized data not escaped

I recently started seeing the following error in my app: "Uncaught SyntaxError: Unexpected token ILLEGAL ". The reported line was to the serialized data in the HTML which, it turns out, contains an unescaped \u2028 character.

JSON.stringify won't escape non-ascii characters so the unicode LINE SEPARATOR is kept as-is. I assumed that the problem was that this was then placed in a non-unicode document however the error is thrown even with the correct headers and meta tags (in Chrome 34.0.1847.131). Not quite sure why that is.

Given the fact that one document is being embedded into another, this may always be problematic, but maybe the easiest path is to just make sure that non-ascii characters in the JSON are escaped. Though I haven't used it before, the ascii-json package seems like a good way to do it.

Superagent call when javascript is Off on the browser

Hi Team,

I am working on the isomorphic application and want to post the request using superagent. It is working fine when javascript is ON. When I made Javascript OFF , post request is not getting triggered.

Snippet :

In XClient.js
addX:function(award){
request
.post('http://localhost:8080/xx')
.send(award)
.set('Accept', 'application/json')
.end(function(res){
if (res.ok) {
alert('yay got ' + JSON.stringify(res.body));
} else {
alert('Oh no! error ' + res.text);
}
});
}

This method is called from input button under Render event from React Component.
Techonogy Used : ReactJs,NodeJS,Superagent

Please suggest , if I have to use some mixin or need to additional handling to make it work at server side.

object is not a function

Hello.

I'm trying to use this with https://github.com/koba04/react-boilerplate

But I'm getting an error saying:

Artist = (0, _reactAsync2['default'])(MyComponentObservables)(Artist) || Art
                                       ^
TypeError: object is not a function

from this

function MyComponentObservables(props) {
  return {
    user: defineXHRObservable(`/api/user`)
  }
}
@Async(MyComponentObservables)
export default class Artist extends React.Component {

Any ideas?

Why componentWillReceiveProps?

Maybe I'm missing something here, but can you clarify why BaseMixin implements the componentWillReceiveProps lifecycle method?

When I update state after initial server-side rendering (which defines props.asyncState), and later update a prop on the parent, this method, will restore the original asyncState.

Currently I need to do this:
delete ReactAsync.Mixin.mixins[0].componentWillReceiveProps

Ability to set props from initialStateAsync

I have a view that gets a list of items asynchronously from an api request. The view has a search box which filters the items by fuzzy matching the name. For this scenario, I'd like the filtered items to always be in the state (this.state.items). When the search is changed it filters the initial items by the search value and sets this.state.items. Optimally I would like to set the initially received items to this.props.items within getInitialStateAsync, however when I do this I receive an error that I cannot set props of an unmounted view.

The workaround I am using is setting this.state.initialItems and using that to run the search against.

Disclaimer: I am a very new to React, so I may be doing this wrong. From what I've read in the docs so far it just seems that props would be the suggested place to store the previous/initial values of state.

[idea] refreshable component?

It's definitely cool that this module can asynchronously fetch data. Would it be possible to also "refresh" itself based on certain conditions?

Distribution sans npm

Hey,

It would be invaluable if react-async were made consumable without npm. Other tooling does exist, such as component which is what I would be interested in. Maybe that's a semi-hard sell until react is itself exposed as a component (https://github.com/reactjs/react-bower/issues/1) so I would be at least happy to have react-async as a stand-alone file.

Thoughts? Thanks.

Escaping issues with __reactAsyncStatePacket?

Hi Andrey,

First, thanks a lot for this library. It makes server-side component rendering really simple!

I'm having some issues with the JSON I've fetched with superagent. The actual JSON is here and it appears to be well-formed.

If you look at the screenshot below, however, something doesn't seem to be escaping correctly. The JSON is a dump of my blog which contains markdown and HTML (including script tags in that markdown).

Is there something I'm missing? Its seems like JSON.stringify is used so I'm not sure what going wrong.

Thanks!

screen shot 2014-04-12 at 17 20 21

render with only component markup

For server-side rendering, it would be useful to have a render method that only returns markup specified by components, without data-reactid attributes and window.__reactAsyncStatePacket, like React.renderToStaticMarkup.

Using renderComponentToStringWithAsyncState gives the warning:

Warning: ReactAsync.renderComponentToStringWithAsyncState will be deprecated in a future version. Use ReactAsync.renderToStringAsync instead.

Is there a way of achieving this, or one in the works?

Related: #14 #15

Bug in nested ReactAsync.Mixin? ("render" called before "getInitialStateAsync")

I've created a repo which shows the odd behaviour: https://github.com/chrisdew/react-quickstart

The problem is that if I click either the link or the button to go to /nesting/users/doe, the NestedUserPage::render is run before (and after) NestedUserPage::getInitialStateAsync.

NestedUserPage::render client.js:125
NestedUserPage::getInitialState client.js:109
NestedUserPage::render client.js:125

In my own code, where it uses Array::map to render lists, this premature rendering produces Uncaught TypeError: Cannot read property 'map' of undefined and stops the site from working.

The un-nested /users/doe works fine, as UserPage::getInitialStateAsync is called before the first UserPage::render.

UserPage::getInitialState client.js:56
UserPage::render 

This may be a bug, or a problem with my understanding.

render a component outside React from client side in an isomorphic app

I asked this question in http://stackoverflow.com/questions/27913004/react-js-render-a-component-from-outside-react and am yet to get any answer and therefore will describe below here.

From http://facebook.github.io/react/docs/component-api.html :

"The only way to get a handle to a React Component instance outside of React is by storing the return value of React.render."

I need to render a React component outside React and the reason for it I'm going to mention below.

In my node.js, expressJS app, I am using 'react-router-component' from here https://github.com/STRML/react-router-component and 'react-async'.

In app.js -the file which is supposed to be run ,

    var url=require('url');   
    var App=require('./react/App.jsx');
    var app = express();
    app.get('*',function(req,res){
    var path = url.parse(req.url).pathname;
    ReactAsync.renderComponentToStringWithAsyncState(App({path:path}),function(err, markup) {
    res.send('<!DOCTYPE html>'+markup);

          });
   });

In App.jsx,

PostList = require('./components/PostList.jsx');
   var App = React.createClass({
   render: function() {
        return (
           <html>
           <head lang="en">
           </head>
        <body>
        <div id="main">

        <Locations path={this.props.path}>

          <Location path="/" handler={PostList} />
          <Location path="/admin" handler={Admin} />
        </Locations>


       <script type="text/javascript" src="/scripts/react/bundle.js"></script>
       <script type="text/javascript" src="/scripts/custom.js"></script>                 

        </body>
        </html>
        }); 

bundle.js is the browserified file from all the .jsx files.

In PostList.jsx,

    var PostList = React.createClass({

        mixins: [ReactAsync.Mixin],

        getInitialStateAsync: function(cb) {

           if(typeof this.props.prods==='undefined'){

            request.get('http://localhost:8000/api/cats_default', function(response) {

                    cb(null, {items_show:response.body});

                       });
                                                    }
        },

        setTopmostParentState: function(items_show){

          this.setState({
             items_show:items_show
                       });

         },

        render: function() {
        return (

              <div className="postList" id="postList">
              **// Things go here** 
              <div className="click_me" >Click me</div>
              </div>    
            }

    });


    PostListRender=function(cart_prods){

        var renderNow=function(){

            //return <PostList  cart_prods={cart_prods}></PostList>

             React.renderComponent(<PostList  cart_prods={cart_prods}></PostList>,document.getElementById('postList') );  
                                 };

         return {
           renderNow:renderNow
                }   
        };
    module.exports=PostList;

In custom.js:

$('.click_me').click(function(){

PostListRenderObj=PostListRender(products_cart_found);
PostListRenderObj.renderNow();

});

The page shows well. But when I click on the click_me element, the browser shows script busy, console shows

ReactJS - ReactMount: Root element has been removed from its original container. New container

And the Firebug log limit exceeds.

So why I want to render on click from outside react.js: I have to run the http://github.hubspot.com/odometer - jQuery Odomoeter plugin. The plugin was not developed as a node middleware although it can be installed the way a middleware is installed and the plugin codebase is saved inside node_modules folder. If I do not require the plugin to be a middleware from inside node and just include it in the page before the browserified JS file and perform the click event inside React, then the odometer animation is not properly shown.

The React site as mentioned in the beginning tells to hold the return value of React.render to use it outside Reatct . But no such method is available in react-async middleware.

So what is the way to render the PostList React component outside React on clicking the click_me div ?

could you add fibers as dependency again?

its optional since version 0.6.0, but we're using renderComponentToStringWithAsyncState which forces us to have fibers in the package.json and confuses people because fibers it not actively used (in our code)

Using session information for server-rendered pages. (Logging in.)

I've based a project around your react-quickstart structure.

I'm developing a login system which store the currently-logged-in-user as a logged_in state at the top level element (above the react-router-component pages). See STRML/react-router-component#98 for more details.

I've run into a hurdle. The top-level App class makes an AJAX call to get its logged_in state.

When the AJAX call is made from a browser, the express-session middleware is used to retrieve the sessions's username, and that is returned to the calling browser.

When the page is rendered server-side (because of a page refresh, for example), the AJAX call is made form the server, to the server, and so doesn't have the session details.

I suppose that what I'm looking for is a way to send the request session cookie through to the AJAX calls the server makes to itself when rendering a page server-side.

Is there a way to do this?

Or is there a better login solution which works for both client and server-rendered pages?

Getting rid of "localhost:3000" URLs

Cross-posted: http://stackoverflow.com/questions/26463924/getting-rid-of-localhost3000-urls-for-reactasync

When using react-async is is common to have code which looks like:

var UserPage = React.createClass({
  mixins: [ReactAsync.Mixin],

  statics: {
    getUserInfo: function(username, cb) {
      superagent.get(
        'localhost:3000/api/users/' + username,
        function(err, res) {
          cb(err, res ? res.body : null);
        });
    }
  },

  getInitialStateAsync: function(cb) {
    this.type.getUserInfo(this.props.username, cb);
  },

  ...

The problem with this is that it only runs correctly in a browser running on the server.

Using the obvious solution of making the URLs relative (e.g. '/api/users/' + username has a subtle issue.

It seems to work, when moving between pages, but does not work on a page reload or initial load. (You do not actually move between pages in a ReactJS app, it's just the URL that changes.)

The cause of this issue is that the server needs to call the AJAX API, during server-side rendering, but the server has no knowledge of the page origin as seen by browsers (http://www.example.com:3000).

Is there a way of telling this to the server-side renderer?

(I have already thought of a nasty work-around, where you use full URLs for both client and server, but this must be configured explicitly for each development, test and production server that runs the code.)

Server: Can we render to a buffer?

I know it could get complicated fast and would require re-implementing a lot of the existing code for it, but if we setup the library for a renderComponentToBufferWithAsyncState then we'd have much better performance as far as memory goes and scaling the process would be much easier.

For example:

app.get('/', function(req, res) {
  ReactAsync.renderComponentToBufferWithAsyncState(App(null), function(err, markupBuffer) {
    if (err) {
      console.error(error);
      res.status(500).end();
      return;
    }

    res.write('<!doctype html>\n');
    res.write(markupBuffer);
    res.end();
  });
});

Unable to stringify Cortex objects

Brief code:
var model = new Cortex({username: 'abc'}, function(updatedBody) { });
JSON.stringify(model);

Result: TypeError: Converting circular structure to JSON

This would especially be useful when rendering on the server. Any solutions to get by this?

peerinvalid cannot satisfy peerDependencies

Have another packages with different peerDependencies on react but it should work with this, I believe. I'm getting this error:

npm ERR! peerinvalid The package react does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants [email protected] - 0.11.x
npm ERR! peerinvalid Peer [email protected] wants react@~0.11.0

As you can see they should overlap at 0.11.0, but npm complains. I don't see very much documentation on this, any ideas?

Node Fibers

Do you think it would be theoretically possible to implement renderComponentToStringWithAsyncState in a way that doens't use fibers?

react warnings that props arent' specified. Expected?

I'm getting some warnings of the form:
Warning: Required prop 'allTodos' was not specified in 'MainSection'. warning.js:43`

This seems logical since the checks probably run before getInitialStateAsync had a chance to return. Just checking: is this expected when using getInitialStateAsync or is there a way around these warnings. I don't like warnings ;)

A couple outdated modules

Steps to reproduce

$ # sudo npm i nsp -g

$ npm shrinkwrap --dev
wrote npm-shrinkwrap.json

$ nsp audit-shrinkwrap
Name          Installed   Patched  Vulnerable Dependency
syntax-error    0.0.1    >= 1.1.1  react-async > connect-browserify > browserify
qs              0.6.6     >= 1.x   react-async > express > connect
qs              0.6.6     >= 1.x   react-async > express > connect
qs              0.6.6     >= 1.x   react-async > phantomjs > request
qs              0.6.6     >= 1.x   react-async > phantomjs > request

$ npm outdated --depth 0 | sort
Package             Current  Wanted  Latest  Location
browserify           3.46.1  3.46.1  5.10.0  browserify
connect-browserify    1.0.0   1.0.0   3.2.0  connect-browserify
express               3.4.8   3.4.8   4.8.3  express
jshint                2.4.4   2.4.4   2.5.3  jshint
mocha                1.17.1  1.17.1  1.21.4  mocha
mochify              0.11.3  0.11.3   1.0.2  mochify
node-dev              2.1.6   2.1.6   2.3.0  node-dev
semver                2.2.1   2.2.1   3.0.1  semver

If you don't want to update to Express 4, you can still use [email protected] which should be patched.

phantomjs should be patched, as of Medium/phantomjs#211

browserify should be pached as of [email protected] (latest is 5.10.0)

Callback for getInitialStateAsync

The documentation and the example files seem to disagree on the parameters passed to the callback:

Doc:

cb(data)

Example:

cb(null, {message: 'Hello'})

client bootstrapping json does not work correctly if page has multiple async components

I am rendering a component on the server. This component has three child components. Each of these child components call getInitialStateAsync. When the page is loaded on the browser, it looks like react async is trying to boostrap first child component with the json that was dumped by the server. However, the json that its attempting to load is from the third child component.

It looks like we can only safely call getInitialStateAsync once on the server. Calling it multiple times overrides the previous json dump. So in my case, each of the child components call getInitialStateAsync and their json dumps override the previous dump. So on the client, react-async is attempting to bootstrap the first child component with the third's json data. The remaining two child components do not get bootstrapped at all.

Issue with Preloaded component

Hi,
I am trying to create an async component with loading indicator. But when I try to use Preloaded component, it throws error, Preloaded is not defined. I could not figure out how to get reference to the Preloaded in my component.
I have included ReactAsync.Mixin in my component.
Please help

[BROKEN] getComponentFingerprint

With React 0.13.x, you cannot access component._rootNodeID nor component._mountDepth and the object _reactInternalInstance specifying those informations isn't available unless the component has been rendered.
As you request for informations before they are available, it would be an idea to depend on something else.

reset Preloaded component and force preloader and getInitialStateAsync call

On an already rendered page is it possible to force Preloaded.preloader and getInitialStateAsync call, without unmounting and mounting react-async components?

I've a use case when I need to implement refresh functionality for react-async components and during refresh I need to display the preloader.

support for react 0.11.2

new to this node/npm/bower/react world

Unable to find a suitable version for react, please choose one:
    1) react#~0.10.0 which resolved to 0.10.0 and is required by react-async#0.9.4
    2) react#~0.11.2 which resolved to 0.11.2 and is required by MyProjectPrefix the choice with ! to persist it to bower.json

window is not defined at render

Getting this error (ReferenceError: window is not defined at render) when attempting to integrate react-async on this branch:

natew/reactor-core@a3dec93

ReferenceError: window is not defined
    at render (/Users/nw/projects/reactor/node_modules/react-async/browser.js:38:0)
    at _renderValidatedComponent (/Users/nw/projects/reactor/node_modules/react/lib/ReactCompositeComponent.js:862:0)
    at ReactCompositeComponentMixin.mountComponent (/Users/nw/projects/reactor/node_modules/react/lib/ReactCompositeComponent.js:578:0)
    at mountComponent (/Users/nw/projects/reactor/node_modules/react/lib/ReactPerf.js:53:0)
    at ReactCompositeComponentMixin.mountComponent (/Users/nw/projects/reactor/node_modules/react/lib/ReactCompositeComponent.js:582:0)
    at mountComponent (/Users/nw/projects/reactor/node_modules/react/lib/ReactPerf.js:53:0)
    at null (/Users/nw/projects/reactor/node_modules/react/lib/ReactServerRendering.js:51:0)
    at perform (/Users/nw/projects/reactor/node_modules/react/lib/Transaction.js:153:0)
    at renderComponentToString (/Users/nw/projects/reactor/node_modules/react/lib/ReactServerRendering.js:50:0)
    at null (/Users/nw/projects/reactor/node_modules/react-app-controller/data-driven.js:74:0)

Still new to nodejs in general and Google isn't giving me much help here. Really love your work with React so far by the way.

this.<function from another mixin> is undefined in browser (isomorphic app)

I've been experiencing a weird issue when using react-async in conjunction with react-router-component in my isomorphic app.

Given the following mixin:

    ...
    MyMixin = {
        foo: function() {
            console.log("hello!");
        }
    }
    ...

and the React class:

     ...
     var MyView = React.createClass({
        mixins: [Async.Mixin, MyMixin],

        getInitialStateAsync: function(cb) {
            this.foo(); //  only works in the browser
            ...
        }
        ...
    });

When mounting, the browser will throw an error saying this.foo() is undefined. The workaround is to do something like (courtesy of @theadam):

    ...
    var MyView = React.createClass({
        mixins: [Async.Mixin, MyMixin],

        getInitialStateAsync: function(cb) {
            var component = this.type && this.type.prototype ? this.type.prototype : this;
            component.foo(); // Works now
            ...
        }
    });

Obviously, the fix is less than ideal when using it in a larger app. Am I doing something wrong or is this an oversight?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.