Skip to content
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

Usage with react-router #25

Closed
danawoodman opened this issue Apr 22, 2015 · 30 comments
Closed

Usage with react-router #25

danawoodman opened this issue Apr 22, 2015 · 30 comments
Labels

Comments

@danawoodman
Copy link

I'm curious as to the preferred usage with react-router or if a different routing approach is more favorable with baobab.

If I run 0.4.x I can get baobab setup easily using the mixin included with the tree:

//...
var state = require('../state');

module.exports = React.createClass({
  mixins: [state.mixin],

  cursors: {
    users: ['models', 'users']
  },
  //...

When upgrading to 1.0.x I'm unable to get things working with the provided docs. I realize that 1.0.x was just released so I'm not sure if getting this working with react-router has been figured out yet.

Is there an idiomatic way to use baobab-react with react-router?

@Yomguithereal
Copy link
Owner

Hello @danawoodman. Could you describe what is making the repo's mixins fail in your case? The repo is indeed not totally stable right now but will be soon.

I guess the way to use baobab-react and react-router depends highly on whether you want to use mixins or higher order components. If you are using mixins I think you can just use both mixins: baobab-react ones and the router's ones.

@danawoodman
Copy link
Author

Right now I'm getting:

ERROR in ./~/baobab-react/mixins.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./dist-modules/mixins.js in /path/to/code/node_modules/baobab-react

@danawoodman
Copy link
Author

I'm passing the tree down the main <RouteHandler> in react-router

@danawoodman
Copy link
Author

I'm using io.js FWIW

@danawoodman
Copy link
Author

// other imports...
var mixin = require('baobab-react/mixins').branch;

module.exports = React.createClass({
  mixins: [mixin],

  cursors: {
    users: ['models', 'users']
  },

  render() {
    // use `this.state.users`
  }
});

@danawoodman
Copy link
Author

Seems to relate to there being no dist-modules folder which seems to be created as a pre-publish step. Is this on npm yet?

@Yomguithereal
Copy link
Owner

No, the package is not on npm yet but will be very soon (tomorrow or friday at most). The fact that you cannot require the files must indeed come from the package not being built when installing it from git with npm. Can you try to run npm install then npm run prepublish in the package's folder (probably node_modules/baobab-react) and see whether this can fix your import issue?

@danawoodman
Copy link
Author

Read my mind. Just did that and got a bit further. It seems the issue is related to react-router integration as if I use it with a plain component it works but if I pass down the tree in a <RouteHandler tree={tree} /> it doesn't seem to pass the tree down properly even though I'm passing down {...this.props} in the handler to the child components.

@Yomguithereal
Copy link
Owner

I don't remember whether the RouteHandler component propagates its props to the component it actually refers. But shouldn't you attach the baobab mixin to a component higher than the RouteHandler?

@danawoodman
Copy link
Author

I'll check to see if I can pass them in when the routes are intialized and report back

On Thursday, Apr 23, 2015 at 6:56 AM, Guillaume Plique notifications@github.com, wrote:

I don't remember whether the RouteHandler component propagates its props to the component it actually refers. But shouldn't you attach the baobab mixin to a component higher than the RouteHandler?


Reply to this email directly or view it on GitHub.

@danawoodman
Copy link
Author

Sweet, figured it out! I was being silly and needed to pass the tree into the route handler:

var tree = require('./tree');

Router.run(Routes, function (Handler, state) {
  React.render(<Handler tree={tree} />, document.body);
});

Hope this is helpful for others using baobab + react-router. Maybe it warrants an addition into the README @Yomguithereal?

Cheers!

@egeozcan
Copy link

egeozcan commented May 4, 2015

What I did is, in the root element which is rendering the Router.RouteHandler, I set the tree property of the context to my stateTree

    import PropTypes from "baobab-react/prop-types";
    let stateTree = window.stateTree = require("./stateTree.js");

    var App = React.createClass({
        childContextTypes: {
             tree: PropTypes.baobab
        },
        getChildContext() {
            return {
                tree: stateTree
            };
        },
        render() {
            return (
                <div id="app">
                    <Router.RouteHandler/>
                    //...

@gersongoulart
Copy link

Would someone be kind to post a complete example of how to use baobab-react with react-router? I'm trying to piece these bits together but it's not working for me - really hard to find out what piece is missing in this puzzle... Pretty please? :)

@egeozcan
Copy link

egeozcan commented May 8, 2015

@gersongoulart

var stateTree = new Baobab({
  stores: {
    whatevers: []
  }
},
{
  shiftReferences: true
});
let App = React.createClass({
    childContextTypes: {
         tree: PropTypes.baobab
    },
    getChildContext() {
        return {
            tree: stateTree //reference to your baobab
        };
    },
    render() {
        //your main component
        return (
            <div>
                <header>
                    <FancyComponent />
                </header>
                <Router.RouteHandler/>
            </div>
        );
    }
});
let routes = (
    <Route name="app" path="/app" handler={App}>
        <Route name="whatever" path="/app/whatever" handler={WhateverComponent}/>
    </Route>
);
Router.run(
    routes,
    Router.HistoryLocation,
    Handler => {
        React.render(<Handler />, document.body); //or whatever
    }
);
//WhateverComponent.jsx
import {branch} from "baobab-react/higher-order";

let WhateverComponent = React.createClass({
    contextTypes: {
        router: React.PropTypes.func
    },
    render() {
        let list = this.props.whatevers.map((we, i) => <span key={i}>{we.foobar}</span>);
        return (<div>{list}</div>);
    }
});

export default branch(WhateverComponent, {
    cursors: { whatevers: ["stores", "whatevers"] }
});

@gersongoulart
Copy link

OMG @egeozcan! It Works! Yay! :) Thank you very much!! Should that be added to the readme file? I'm sure it'd be of great help for people first arriving at this project!

@egeozcan
Copy link

egeozcan commented May 8, 2015

You're welcome. I think this is wiki material but I'm not the one to decide^^

@danawoodman
Copy link
Author

@egeozcan why not pass in the tree to the <Handler/> in Router.run like I had posted? Seems more explicit that way but I guess I'm not sure if the differences, if any.

@egeozcan
Copy link

@danawoodman because it did't work on my machine© 😄 Seriously, the routing got broken after the first successful navigation (clicking on any link worked once, then page url kept on responding but route context not), for some strange reason. Didn't investigate too much, will post an update if I find out why.

@neotrinity
Copy link

Nice one @egeozcan saved me a load of time! This is definitely wiki material.

@Yomguithereal
Copy link
Owner

Will probably have time to rewrite the docs this week end. I'll include this example while scaffolding a little wiki.

@larryhengl
Copy link

I have used the mixin approach mentioned earlier without a hitch. But now I am wondering how to manage the case where you change tree instances?

If I release the tree, reassign it to a new baobab, and attach new update handlers, the baobab side is fine. but on the react side, despite the attempt of the root mixin to reassign the childContext it got from the route handler tree prop, the child still seems to hold a ref to the original context tree. this causes the cursor listeners to be lost in all the child components.

so if you do this, where tree is a baobab:

Global.tree = require('./tree');

Router.run(Routes, function (Handler, state) {
  React.render(<Handler tree={Global.tree} />, document.body);
});

and Routes:

<Route handler={Page}>
  <Route name="home" path="/" handler={Home} />
  <Route name="users" path="users/:id" handler={Users} />
  <NotFoundRoute handler={Oops} />
</Route>

with Page:

var mixin = require('baobab-react/mixins').root;
module.exports = React.createClass({
  contextTypes: {
    router: React.PropTypes.func.isRequired
  },
  mixins: [mixin],
  render() {
      return (
        <RouteHandler />
      );
  }
});

and Users:

var mixin = require('baobab-react/mixins').branch;
module.exports = React.createClass({
  mixins: [mixin],
  cursors: {
    users: ['models', 'users']
  },
  render() {
    // use `this.state.users`
  }
});

then say,
Global.tree.release();
Global.tree = new Baobab(defaults, options);
and then route back to Users
./users/123

...then the Users component nextState remains the same, not changed via the cursor. And if you try to look at the tree context you get an error:

screen shot 2015-07-09 at 5 47 13 pm

which I suspect is holding a ref to the old tree/cursors/facets when the component was mounted.

i don't know if there is an issue with the way the child component is setting the tree context, or how the listeners are referenced when the component is updated...after getting a different tree context.

or maybe I'm messing up the way react-router should work with baobab?

thoughts?

@Yomguithereal
Copy link
Owner

Hello @larryhengl. I need to investigate a little bit to see whence this problem comes. I'll open a dedicated issue for this one (#67).

Out of curiosity, why do you need to swap the tree at runtime?

@larryhengl
Copy link

@Yomguithereal thanks for looking into this!

the idea of swapping trees at runtime comes from an app i'm working on that does db searches, where i want to serve search results as separate trees (or a reset of the single tree used in the app). this allows for various ui interactions to facet/filter the results and manage active filter state in the ui for the given results. i didn't want to persist the app state across searches nor cache the search data per se - but if i wanted to do that then modeling the tree differently may obviate the need to reset the tree.

@Yomguithereal
Copy link
Owner

I guess that a workaround for now would be to completely change the data of your tree likewise:

tree.set(newData);

@larryhengl
Copy link

right, that does the trick.
somehow i had it in my head that that would bork the facets i had defined, but they were fine (correctly updated). however, since there is no "silent" set option, all the emitters fired. so the challenge then was to either turn off the update event handlers (and add them back after the set) or do better null data handling. i did the latter. :-)
thanks for the help, and the lib!

@manuelmazzuola
Copy link

With the new version of react-router I can't pass props to the compononet.
I can't do anymore <Handler tree={Global.tree} />

Now I have

React.render(                                                                                                          
  <Router>                                                                                                             
    <Route path="/" component={Layout}>                                                                                
      <IndexRoute component={Landing}/>                                                                                
    </Route>                                                                                                           
  </Router>                                                                                                            
  , document.getElementById('content')                                                                                 
);  

How can I pass the tree down to the components ?

@Yomguithereal
Copy link
Owner

@manuelmazzuola, with wrappers component you might try something like:

var Root = require('baobab-react/wrappers');

React.render(
  <Root tree={myTree}>                                                                                                          
    <Router>                                                                                                             
      <Route path="/" component={Layout}>                                                                                
        <IndexRoute component={Landing}/>                                                                                
      </Route>                                                                                                           
    </Router>
  </Root>                                                                                                            
  , document.getElementById('content')                                                                                 
);  

Or create a component wrapping the router or some part of it and root it with mixins or higher-order components.

@philmday
Copy link

@manuelmazzuola : i tried your method however i'm getting the following error:

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).warning @ bundle.js:27495ReactElementValidator.createElement @ bundle.js:181482.baobab @ bundle.js:168s @ bundle.js:1e @ bundle.js:1(anonymous function) @ bundle.js:1
bundle.js:27051 Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

@manuelmazzuola
Copy link

I've resolved with

const createBaobabComponent = (Component, props) => {
  return <Component {...props} tree={tree} />
};


React.render(
  <Router createElement={createBaobabComponent}>
    <Route path="/" component={Layout}>
    </Route>
  </Router>
  , document.getElementById('content')
);

@philmday
Copy link

@manuelmazzuola you are a beautiful person, thank you so! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants