Git Product home page Git Product logo

react-router-relay's Introduction

react-router-relay Travis npm

Relay integration for React Router.

This library only supports Relay Classic. For Relay Modern support, see Found Relay.

This library does not support React Router v4, because React Router v4 does not provide the necessary integration points for efficient data fetching with Relay. Additionally, rendering performance will be better with React Router v2 than with React Router v3, as the link subscription logic in React Router v3 triggers unnecessary rerenders with Relay.

Discord

Usage

Apply the useRelay router middleware, pass in a Relay environment to <Router>, then define Relay queries and render callbacks for each of your routes:

import { applyRouterMiddleware, /* ... */ } from 'react-router';
import useRelay from 'react-router-relay';

/* ... */

const ViewerQueries = {
  viewer: () => Relay.QL`query { viewer }`
};

const WidgetQueries = {
  widget: () => Relay.QL`query { widget(widgetId: $widgetId) }`
}

ReactDOM.render(
  <Router
    history={history}
    render={applyRouterMiddleware(useRelay)}
    environment={Relay.Store}
  >
    <Route
      path="/"
      component={Application}
      queries={ViewerQueries}
    >
      <Route path="widgets">
        <IndexRoute
          component={WidgetList}
          queries={ViewerQueries}
          prepareParams={prepareWidgetListParams}
        />
        <Route
          path=":widgetId"
          component={Widget}
          queries={WidgetQueries}
          render={({ props }) => props ? <Widget {...props} /> : <Loading />}
        />
      </Route>
    </Route>
  </Router>,
  container
);

react-router-relay will automatically generate a combined Relay query config with all queries and parameters from the active React Router routes, then pass down the query results to each of the route components. As the queries are all gathered onto a single query config, they'll all be fetched in parallel, and the data for your entire page will load and then render in one go.

You can find an example implementation of TodoMVC with routing using react-router-relay at https://github.com/taion/relay-todomvc.

Guide

Installation

$ npm install react react-dom react-relay react-router
$ npm install react-router-relay

Router configuration

Apply the useRelay router middleware, and pass in a Relay environment to the <Router>:

import useRelay from 'react-router-relay';

/* ... */

ReactDOM.render(
  <Router
    history={history}
    routes={routes}
    render={applyRouterMiddleware(useRelay)}
    environment={Relay.Store}
  />,
  container
);

Routes and queries

Basic configuration

For each of your routes that requires data from Relay, define a queries prop on the <Route>. These should be just like the queries on a Relay query config:

const ViewerQueries = {
  viewer: () => Relay.QL`query { viewer }`
};

const applicationRoute = (
  <Route
    path="/"
    component={Application}
    queries={ViewerQueries}
  />
);

Just like with Relay.Renderer, the component will receive the query results as props, in addition to the other injected props from React Router.

If your route doesn't have any dependencies on Relay data, just don't declare queries. The only requirement is that any route that does define queries must have a Relay container as its component.

If your route's Relay data dependencies are a function of the location or of the parameters, you can define a getQueries function on your route that returns the computed queries as a function of the current router state:

<Route
  component={Widget}
  getQueries={({ location, params }) => getWidgetQueries(location, params)}
/>

Path parameters

Any path parameters for routes with queries and their ancestors will be used as parameters on the Relay query config:

const WidgetQueries = {
  widget: () => Relay.QL`
    query {
      widget(widgetId: $widgetId), # $widgetId comes from the path.
    }
  `
}

const Widget = Relay.createContainer(/* ... */, {
  fragments: {
    widget: () => Relay.QL`
      fragment on Widget {
        name,
      }
    `
  }
});

// This handles e.g. /widgets/3.
const widgetRoute = (
  <Route
    path="widgets/:widgetId"
    component={Widget}
    queries={WidgetQueries}
  />
);

Additional parameters

If your queries require additional parameters from the location, such as from the location query or state, you can add those parameters with prepareParams. You can also use prepareParams to do additional conversion or initialization of your parameters.

The prepareParams method has the same signature and behavior as prepareParams on a Relay query config, except that it also receives the current router state as an argument. It is expected to return the same result when given the same previous params and router state.

Additionally, you can use route parameters as variables on your containers:

const WidgetList = Relay.createContainer(/* ... */, {
  initialVariables: {
    color: null,
    size: null,
    limit: null
  },

  fragments: {
    viewer: () => Relay.QL`
      fragment on User {
        widgets(color: $color, size: $size, first: $limit) {
          edges {
            node {
              name,
            },
          },
        },
      }
    `
  }
});

function prepareWidgetListParams(params, { location }) {
  const { color, size } = location.query;
  const limit = location.state && location.state.limit;

  return {
    ...params,
    color,
    size: size && parseInt(size, 10),
    limit: limit || 10,
  };
};

// This handles e.g. /widgets?color=blue&size=3.
const widgetListRoute = (
  <Route
    path="widgets"
    component={WidgetList}
    queries={ViewerQueries}
    prepareParams={prepareWidgetListParams}
  />
);

Named components

For routes with named components, define queries as an object with the queries for each component by name:

<Route
  components={{ foo: FooComponent, bar: BarComponent }}
  queries={{ foo: FooQueries, bar: BarQueries }}
/>

Render callback

You can pass in a custom render callback to your routes:

<Route
  component={WidgetList}
  queries={ViewerQueries}
  render={({ props }) => props ? <WidgetList {...props} /> : <Loading />}
/>

This has the same signature as the render callback on Relay.Renderer, except that, when present, props will also include the injected props from React Router. As on Relay.Renderer, you can return undefined to continuing rendering the last view rendered.

While transitioning, the ready state properties (done, error, retry, stale) will reflect the ready state of the transition as a whole. However, the props object in the render callback for a route will be populated as long as the data for that particular route are ready. For example, if a transition does not change the params to the queries for a parent route, the render callback for that route will have props, even while the render callbacks for its child routes may not.

The argument object to the render callback includes two extra properties: routerProps and element. routerProps contains the props from the router, and unlike props, will be populated regardless of the Relay ready state. routerProps can be used to render child routes while the data for the parent route are still loading, or to otherwise use information from the router to control rendering before the Relay data are available. element contains the base route element without the props from Relay, and can be used to render the route component when the route uses a dynamic component, or when using other router middlewares that may wrap the route component:

({ props, routerProps, element }) => {
  if (!props) {
    return <Loading {...routerProps} />;
  }

  return React.cloneElement(element, props);
}

When using named components, you can define these on a per-component basis, optionally omitting the callback for components that do not need a custom render callback:

<Route
  components={{ foo: FooComponent, bar: BarComponent }}
  queries={{ foo: FooQueries, bar: BarQueries }}
  render={{ foo: renderFoo }}
/>

Additional Relay.Renderer configuration

We pass through additional props on <Router> or the generated router context to the underlying Relay.Renderer. You can use this to control props like forceFetch on the Relay.Renderer:

<Router
  history={history}
  routes={routes}
  render={applyRouterMiddleware(useRelay)}
  forceFetch={true}
  environment={Relay.Store}
/>

Notes

  • The default Relay network layer executes each query as a separate network request. If you want to execute all of your queries in a single request, then you will need to use a custom network layer that can send multiple queries in the same request, along with a GraphQL server that can execute multiple queries from a single request.
  • react-router-relay uses referential equality on route objects to generate unique names for queries. If your route objects do not maintain referential equality, then you can specify a globally unique name property on the route to identify it.
  • Relay's re-rendering optimizations only work when all non-Relay props are scalar. As the props injected by React Router are objects, they disable these re-rendering optimizations. To take maximum advantage of these optimizations, you should make the render methods on your route components as lightweight as possible, and do as much rendering work as possible in child components that only receive scalar and Relay props.

Authors

react-router-relay's People

Contributors

callahanchris avatar chentsulin avatar colllin avatar cpojer avatar denvned avatar devknoll avatar enaqx avatar guncha avatar josephsavona avatar jsierles avatar neitsch avatar nevilles avatar nodkz avatar ooflorent avatar sgwilym avatar simondegraeve avatar taion 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-router-relay's Issues

TypeError: query is not a function

Hi!
I'm new to react, so sorry if this is some noob error.
I'm trying to get my head arround the Relay todo example, and I'm messing around with the routes, trying to change from JSX syntax to plain route objects.
This is my code:

let RouteAsd = [{
    path:'/',
    component: TodoApp,
    indexRoute:{
        component: TodoList,
        prepareParams(){return({status: 'any'});},
        queries:{ViewerQueries}
    },
    childRoutes: [ {
        path: ':status',
        component: {TodoApp},
        queries:{ViewerQueries}
    } ]
}]

ReactDOM.render(
    <RelayRouter
        history={createHashHistory({queryKey: false})}
        routes={RouteAsd}
    />,
document.getElementById('root')
);

In the browser console, I get the following error:

TypeError: query is not a function

That links to the following line:

return query(component, routeParams);

And it's coming from react-router-relay (RouteAggregator.js, line 97).

I'm using react-router 1.0.2 and react-router-relay 0.8.0.

Did I make some silly mistake in the code or this is a react-router-relay related bug?
Thanks!

React router style URL parameters

React router supports the definition of parameters directly in the path, as per the react-router docs:

// from the path /inbox/messages/:id
var id = this.props.params.id;

Would be nice to be able to pass in queryParams={['id']} and have this work.

willTransitionTo not working

What is the equivalent of willTransitionTo in react-router-relay? I want to do something like:
static willTransitionTo(transition) {
if (!LoginStore.isLoggedIn()) {
transition.redirect('/login', {}, {'nextPath' : transition.path});
}
}

Thx

Add Flow annotations

I think this is the next big thing we should add to this repo. Relay is fully typed with Flow and we should make that a standard for library around Flow.

How to pass props in createElement

I have been using createElement on react-router to pass intlData for react-intl. But thats not working anymore with relay-nested-routes. Application does not get these intlData.

Does it seem like possible bug in relay-nested-routes?

function createElement(Component, props) {
    if (Component === Application) {
        return <Component {...props} {...intlData} />
    }
    return <Component {...props} />
}

Allow passing in the Router component.

Could there maybe be a prop to RelayRouter that lets us pass down the Router component that should be rendered? If it isn't specified, could probably default to the react-router Router. This would be useful so we could use things like ReduxRouter or other custom routers.

If you approve, I'd like to make a PR for this. :)

Support server-side rendering

Currently every Relay Container rendered on server side appears as <noscript> tag. This happens because child elements returned from Container are always null and causing error TypeError: Cannot read property 'firstChild' of undefined (sample repo here). Please provide a way to delay fetching data as described in #36 (comment). I know that RelayStore.primeCache that is called by the internals of RelayRootContainer currently doesn't accept this but that can be easly fixed even locally.

Support async React Router components

React Router now has the ability to do code splitting via Webpack fairly easily (https://github.com/rackt/react-router/tree/master/examples/huge-apps).

When I try using the following code with react-router-relay, I get duplicate HTTP requests to /graphql when I navigate to the /widget/:id route.

let Routes = {
  childRoutes: [
    { name: 'home',
      path: '/',
      component: App,
      queries: HomeQueries
    },
    { name: 'widget',
      path: '/widget/:id',
      queries: WidgetQueries,
      getComponents (cb) {
        require.ensure([], (require) => {
          cb(null, require('./components/Widget'))
        })
      }
    }
  ]
};

ReactDOM.render(
  <Router
    history={new BrowserHistory()}
    createElement={ReactRouterRelay.createElementFunction()}
    routes={Routes}
  />,
  document.getElementById('root')
);

I'm trying to figure out a solution/workaround. I'll update the discussion with my findings.

Turn a clean url segment into a global id for node field look up

I would like to call a base64 function on a clean URL slug (e.g http://red-badger.com/blog/2015/07/09/graphql-and-the-open-movie-database-from-introspection-to-inception/) so I can look up our Post node using the node query field instead instead of the custom query field we made getPostBySlug. This would mean our SPA uses Relay's caching. Is this possible with relay-nested-routes? Do I have a hook I can use to do some computation on url segment and pass a new variable to the query? Here is the code I have at the moment. Thanks.

let PostQueries = {
  post: Component => Relay.QL`
    query {
      getPostBySlug(slug: $splat) {
        ${Component.getFragment('post')},
      },
    }
  `
};

React.render(
  <Router history={new BrowserHistory()}>
    <Route component={NestedRootContainer}>
      <Route
        name="home" 
        path="/blog"
        component={PostIndex}
        queries={IndexQueries}
      />
      <Route
        name="post"
        path="/blog/*"
        component={Post}
        queries={PostQueries}
      />
    </Route>
  </Router>,
  document.getElementById('root')
);

Router params and fragments example needed in README

The README skims very quickly over working with react-router’s params and queries. I'm trying to do something everyone seems to have bumped into, which is to pass on some slugs from the route params to a container (as we're all working around this viewer root field for now). I've not had any luck combining #16 and facebook/relay#99.

It would be great if the README could include an example of this with all of the required pieces: route, query and container.

I need a bigger example please

Define an object containing your queries that a particular Relay.Container needs and add it as a queries prop to any container s.

It's too vague for me. My Relay.Container already has fragments specified, is that the query? Do I need a Relay.Route at any point or is that auto generated? Based on the queries?

The takes a prop "component", while the original react-router takes "handler". I take it the component is supposed to be a Relay container not... a component? Or does it actually take a component and the Relay container is auto generated?

Also the example could include the imports

import {Router, Route} from 'react-router';
import BrowserHistory from 'react-router/lib/BrowserHistory';

(Initially I had no idea where to get the BrowserHistory from, for example)

Thanks. The router seems awesome, just can't seem to figure it out and the source is a little over my head I guess.

Add working example

I run into some issue when i try to get params in relay container, i follow the docs as careful as i can, wondering can you provide some examples ?

thx a lot !

Passing variables to child container from parent container

Let's say I have theses routes, with a global widget list on / and specific widget lists on /widgets/:WidgetListID.

<Route
    path='/' component={Layout}
  >
    <IndexRoute
      component={WidgetListContainer}
      queries={ViewerQueries}
    />
    <Route
      path='/widgets/:WidgetListID'
      component={WidgetListContainer}
      queries={ViewerQueries}
    />
  </Route>

It's the same <WidgetList/> component, rendered inside <WidgetListContainer/> inside <Layout/> and here is how I try to pass the WidgetListID variable:

*Layout.js*
class Layout extends React.Component {
  render() {
    return (
      <div>
       *...*
        {children}
      *...*
      </div>
    );
  }
}
*WidgetListContainer.js*
class WidgetListContainer extends React.Component {
  render () {
    return (
      <div>
       *...*
        <WidgetList 
          viewer={viewer}
        />
      </div>
    )
  }
}

export default Relay.createContainer(WidgetListContainer, {
  initialVariables: {
    WidgetListID: null
  },
  fragments: {
    viewer: ($WidgetListID) => Relay.QL`
      fragment on User {
        ${WidgetList.getFragment('viewer', $WidgetListID)}
      }
    `,
  },
})
*WidgetList.js*
class WidgetList extends React.Component {
  render () {
    return (
      <div>
        <ul>
          {viewer.widgets.edges.map(edge =>
            <li key={edge.node.id}>{edge.node.widget.name}</li>
          )}
        </ul>
      </div>
    )
  }
}
export default Relay.createContainer(WidgetList, {
  initialVariables: {
    WidgetListID: null
  },
  fragments: {
    viewer: () => Relay.QL`
      fragment on User {
         widgets(first: 10, WidgetListID:$WidgetListID) { 
          edges { 
            node { 
              id, 
              name
            } 
          } 
        } 
      }
    `,
  },
})

I have no problem setting the WidgetListID variable directly inside the WidgetList relay container, it works perfectly fine, but as soon as I try to pass it from the WidgetListContainer relay container I have an empty data object {__dataID__: "VXNlcjo="}. Though, the variable is well printed in my getWidget() function. So something doesn't work at some point but I can't figure out what?

Nested queries

const messagesQueries = {
  me : () => Relay.QL`query { me }`
};

const inboxQueries = {
  me : () => Relay.QL`query { me }`
};
<Route
  path="/org/:orgId"
  getComponent={(location, callback) => require.ensure([], require => {
    callback(null, require('./Messages'));
  })}
  queries={messagesQueries}
>
  <Route path="inbox">
    <IndexRoute
      queries={inboxQueries}
      getComponent={(location, callback) => require.ensure([], require => {
        callback(null, require('./InboxList'));
      })}
    />
  </Route>
</Route>
// InboxList Fragments
fragments : {
  me : () => Relay.QL`
    fragment on Account {
      organizations {
        id,
        savedThreadQueries {
          id
        }
      }
    }
  `
}
// Messages Fragments
fragments : {
  me : () => Relay.QL`
    fragment on Account {
      organizations {
        id
      }
    }
  `
}

Using this setup, the generated query looks something like this:

query Routes {
  me {
    id,
    ...__RelayQueryFragment0wpqdoa
  }
}

fragment __RelayQueryFragment0wpqdoa on Account {
  id,
  organizations { id }
}

But I think it should look like

query Routes {
  me {
    id,
    ...__RelayQueryFragment0wpqdoa
  }
}

fragment __RelayQueryFragment0wpqdoa on Account {
  id,
  organizations { id, savedThreadQueries { id } }
}

I don't know if I'm just using it wrong or what else might be happening that I don't understand properly yet.

Diagnosing failure after upgrading to v0.8.0

Hey, How can I diagnose a silent failure after upgrading both react-router-relay and react-router?

I was already on [email protected], but upgraded to [email protected], [email protected], and [email protected].

I switched to using <RelayRouter>, but now one of my routes doesn't correctly render its children.

I have something like...

ReactDOM.render(
    <RelayRouter
        history={createBrowserHistory()}
    >
        <Route path="/" component={AdminApp}>
            <IndexRoute component={CampaignList} queries={ViewerQueries} />
            <Route path="/campaigns/:campaignId" component={Campaign} queries={CampaignAndViewerQueries}>
                <IndexRoute component={CampaignMain} queries={CampaignAndViewerQueries} />
                <Route path="geometries" component={CampaignGeometries} queries={CampaignAndViewerQueries}>
                    <Route path="upload" component={CampaignGeometriesUpload} queries={CampaignQueries} />
                </Route>
            </Route>
        </Route>
    </RelayRouter>,
    document.getElementById('app-root')
);

And CampaignList renders correctly at the root (/), but at /campaigns/:id, Campaign doesn't render. My best guess is that it has something to do with having deeper nesting? Since CampaignList doesn't have any child routes but Campaign does.

Problems with Relay 0.2.1

Just updated to Relay 0.2.1 and trying to view any of my routes now gives me the following:

Invalid prop `Component` supplied to `RelayRootContainer`, expected a RelayContainer. Check the render method of `ReactRouterRelay.RootComponent`.

and

Uncaught TypeError: Component.hasFragment is not a function

Seems like Relay isn’t happy about what react-router-relay is passing along. Anyone having the same problem?

router instance

how is it possible to create router using Router.create to pass around to components? I am trying to pass router to my custom network layer to redirect to login when server authentication failed.

Route props as query variables

It does not seem to currently be possible to assign a relay query with parameters outside of those in the url (path and query string params). Is there any interest in being able to pass these variables down via properties on the route?

My reason for this is to pass auth data to a query without having it be part of the url. What i would like to have is something like the following.

<Router history={history} createElement={ReactRouterRelay.createElement}>
            <Route path="/"
                    component={Profile}
                    queries={userQuery}
                    authData={authData}/> 
<Router/>

If this is something that others would like to have as well i would be more than happy to open a pull request with this addition. Please tell me if there are better ways to achieve similar results.

ReactRouterRelay.Container to pass down props?

For application state it's sometimes useful to clone children with props like a flux or redux store. However, this just means I'm putting those props on the ReactRouterRelay.Container — would it be possible for it to pass those props on?

Fix how we use prepareParams

There are way too many edge cases with the current implementation of prepareParams when using nested routes.

For both RouteAggregator and <RouteContainer>, we should probably first figure out all the raw param values, then run every relevant prepareParams hook on the route chain.

Parameter binding on (root) queries broken in 0.7?

Passing parameters to a top-level relay query (as in the first example from the README) seems to work on 0.6.2, but fail on 0.7.0. On 0.7.0 no parameter value is passed to widget. Downgrading to 0.6.2, the parameter is passed as expected.

e.g.

const WidgetQueries = {
  widget: () => Relay.QL`
    query {
      widget(widgetId: $widgetId) # `widgetId` receives a value from the route
    }
  `
}

const widgetRoute = (
  <Route
    path="widgets/:widgetId" component={Widget}
    queries={WidgetQueries}
  />
);

Invariant Violation: RelayQueryNode: Invalid concrete node

react-router-relay does not seem to be working, even with a basic example. Below is the entire (extremely simple) app I'm testing with and it always throws the error:

Invariant Violation: RelayQueryNode: Invalid concrete node
invariant   @   app.js:27826
createNode  @   app.js:29665
(anonymous function)    @   app.js:28894
getChildren @   app.js:28893
hasDeferredDescendant   @   app.js:28948
hasDeferredDescendant   @   app.js:29097
(anonymous function)    @   app.js:40346
splitAndFlattenQueries  @   app.js:40345
(anonymous function)    @   app.js:40441
(anonymous function)    @   app.js:34667
(anonymous function)    @   app.js:34673
invokeWithinScopedQueue @   app.js:34716
originalQueue   @   app.js:34706
scheduleIfNecessary @   app.js:34694
await   @   app.js:34679
runQueries  @   app.js:40438
run @   app.js:40312
primeCache  @   app.js:40158
_runQueries @   app.js:43117
RelayRootContainer  @   app.js:43054
ReactCompositeComponentMixin.mountComponent @   react.js:5438
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
ReactCompositeComponentMixin.mountComponent @   react.js:5514
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
ReactCompositeComponentMixin.mountComponent @   react.js:5514
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
ReactCompositeComponentMixin.mountComponent @   react.js:5514
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
ReactCompositeComponentMixin.mountComponent @   react.js:5514
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
ReactCompositeComponentMixin.mountComponent @   react.js:5514
ReactPerf.measure.wrapper   @   react.js:12166
ReactReconciler.mountComponent  @   react.js:12791
mountComponentIntoNode  @   react.js:10691
Mixin.perform   @   react.js:15445
batchedMountComponentIntoNode   @   react.js:10707
Mixin.perform   @   react.js:15445
ReactDefaultBatchingStrategy.batchedUpdates @   react.js:8308
batchedUpdates  @   react.js:13520
ReactMount._renderNewRootComponent  @   react.js:10894
ReactPerf.measure.wrapper   @   react.js:12166
ReactMount._renderSubtreeIntoContainer  @   react.js:10963
ReactMount.render   @   react.js:10983
ReactPerf.measure.wrapper   @   react.js:12166
(anonymous function)    @   app.js:171
__webpack_require__ @   app.js:20
i   @   app.js:40
(anonymous function)    @   app.js:43

Here is a basic app that I'm testing with.

/* All the dependencies */
import ReactRouterRelay from 'react-router-relay';
import { Router, Route } from 'react-router';
import createHashHistory from 'history/lib/createHashHistory';

const history = createHashHistory({queryKey: false});

/* Basic React component */
class App extends React.Component {
    render() {
      return (
        <div>
        <h1> {this.props.relay.route.name}</h1>
          <ul>
          {this.props.viewer.content.map((item, index) => {
            return <li key={index}> {item.value} </li>;
          })}
          </ul>
        </div>
      );
    }
  }

const appComponent = Relay.createContainer(App, {
  fragments: {
    viewer: () => Relay.QL`
      fragment on viewer {
        content  {
           value
        }
    }
    `
  }
});

/* The basic query */
const viewerQueries = {
  viewer: () => Relay.QL`query { viewer }`
};

/* Super simple route */
const routes = (
  <Route path="/" component={propertyComponent} queries={viewerQueries}/>
);


ReactDOM.render((
  <Router
    createElement={ReactRouterRelay.createElement}
    history={history} routes={routes}
  />),
  document.getElementById('root')
);

And the relevant dependencies from package.json:

    "history": "^1.11.0",
    "react": "^0.14.0-rc1",
    "react-dom": "^0.14.0-rc1",
    "react-relay": "^0.3.2",
    "react-router": "^1.0.0-rc1",
    "react-router-relay": "^0.5.0"

Support for stock Relay.route instead of Query

Hi,
I tried to use a Relay.route instance as value for Query param on a Route like this:

class ViewerQuery extends Relay.Route {

  static routeName= 'ViewerQuery'

  static paramDefinitions = {
    userID: {required: true},
  }

  static queries = {
    user: () => Relay.QL`query { user(id: $userID) }`,
  }
}

var ViewerQuery = new ViewerQuery({userID: userID});

and then on my Route

<Route path='/' component={Index} queries={ViewerQuery} renderFailure={function(error, retry) {console.log(error); }} >

It throw an error 'query is not a function' from routeAggregator.js
It would be great if we could avoid path='/:userID' to get the userID params in our query.
Do you plan to support this feature?
Thanks for what you've done so far! ;)

Caveats

Relay containers attempt to avoid re-rendering except when necessary. However, they can only do so when all props not through Relay are of scalar types. As the props injected by Relay Router into route components are not of static types, this optimization does not work there. As such, when using React Router with Relay, you should attempt to make the render method on any route components as lightweight as possible, and leave the real rendering work to child components that only receive scalar non-Relay props.

I don't understand what are you saying in the Caveats section.
Can you provide an example there?

support babel 6

would be good to upgrade this to support babel 6. Currently it can only run on babel 5

Children routes not rendered after updating to 0.8.0

First heres what I'm running:

    "react": "0.14.3",
    "react-dom": "^0.14.3",
    "react-relay": "0.5.0",
    "react-router": "1.0.0",
    "react-router-relay": "0.8.0",

My routes

ReactDOM.render(
  <RelayRouter history={createBrowserHistory()} routes={routes} />,
  document.getElementById('root')
);
export default (
  <Route
    path="/"
    component={App}
    queries={ViewerQueries}
  >
    <IndexRoute
      component={InsyteList}
      queries={ViewerQueries}
    />
    <Route
      path="insyte/:id" component={Insyte}
      queries={InsyteQueries}
    />
  </Route>
)

The IndexRoute is always the one that renders even when hitting urls like #/insyte/123.
Actually any route will always render the index route even if I type something like /#/aadfasdfsdasdf
When I inspect route aggregator i see this for the route:

route: Object
name: "$$_aggregated-$$_route[1]_viewer-$$_route[2]_viewer"
params: Object
__proto__: Object

Thought it was related to #76 but my queries seem OK.
You can take a look at the actual code here if it helps: https://github.com/xuorig/insyto/tree/master/js

variables

for the following query:

var number = "1232321";

export default {
    auth: () => Relay.QL`query queryType ($number: String) { calc (number: $number ) }`,
}

and the following route:

<Route
      path="calc" component={Calc} 
      queries={myQueries} }
    />

how should I pass my $number variable to the query?
currently, I am get the following error:

Uncaught Error: Invariant Violation: callsFromGraphQL(): Expected a declared value for variable, `$number`.

thanks

Route props as query variables

It does not seem to currently be possible to assign a relay query with parameters outside of those in the url (path and query string params). Is there any interest in being able to pass these variables down via properties on the route?

My reason for this is to pass auth data to a query without having it be part of the url. What i would like to have is something like the following.

<Route path="profile"
    component={Profile}
    queries={userQuery}
    authData={authData}/> 

If this is something that others would like to have as well i would be more than happy to open a pull request with this addition. Please tell me if there are better ways to achieve similar results.

Make note of RelayContainer + route handler caveats

From conversation with @josephsavona on Reactiflux#relay -

RelayContainer attempts to not re-render the component if nothing has changed, by doing a limited shallow equality check that always treats non-scalar values as being unequal.

React Router injects non-scalar props into all route handlers, and we preserve this behavior. Therefore, route handlers that are Relay containers will re-render more often than they otherwise should. The correct thing to do is to move all the "real" rendering logic out of the route handler components and into children of those components.

Given the overlap, I think makes sense to document that caveat here. I'd open a PR to do so, but haven't thought of a good, easy-to-understand way to describe the problem yet.

URL route param vs descriptive label

Say I have a component UserPage whose route takes a userId parameter which is a serialized string that is used to fetch the data for a user.

/user/:userId

But I would like to show a URL with the user's name.

/user/rocketman

The route would need to be supplied with the userId and userName params, but only show the userName param in the URL bar and only pass the userId param to the query.

Are there any APIs for this or thoughts for adding it?

query is not a function in RouteAggregator

I've been getting this query is not a function error, which I've captured below. I added a log in the Object.keys(queries).forEach call and also logged out queries before that.

screen shot 2015-09-28 at 11 21 32 am

This call seems to assume query is a function without checking so, which is causing the error. Below is my setup. Note that I've also tried exporting a simple object from viewerQueries like is done in the todoMVC example, and its the same issue.

app.js

import Application from './components/App';
import Property from './components/Property';
import Relay from 'react-relay';
import ReactDOM from 'react-dom';
import {Router, Route} from 'react-router';
import ReactRouterRelay from 'react-router-relay';
import ViewerQueries from './queries/ViewerQueries';


Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('http://localhost:3000/')
);

ReactDOM.render(
  <Router createElement={ReactRouterRelay.createElement}>
    <Route
      path="/" component={Application}
      queries={new ViewerQueries()}>
      <Route path='properties' component={Property} queries={new ViewerQueries()}/>
    </Route>
  </Router>,
  document.getElementById('root')
);

queries/ViewerQueries.js

import Relay from 'react-relay';

class ViewerQuery extends Relay.Route {
  static queries = {
    viewer: () => Relay.QL`
    query { viewer }`
  };
  static routeName = 'ViewerQuery'
}

export default ViewerQuery;

Invariant Violation: RelayQueryNode: Invalid concrete node.

I'm getting an error "Invariant Violation: RelayQueryNode: Invalid concrete node." and my route isn't rendering when I try to view the relay route below ("test/VXNlcjoxMDI1").

I've no idea what's going on.

The static route renders fine, so it's not an issue with my router setup. If I remove the userId parameter from the URI and hardcode the initial value of the userId I get the same error, so it's not an issue with extracting the userID from the URI. If I turn the App into a relay component and add a queries property I get the same bug, only this time the app doesn't render at all.

So it seems there's a problem for some reason in my config with using relay routes.

Is this a bug or a problem with my setup? I've checked the docs again and again and I think I'm following them exactly.

Here's my code. app.js:

import {SimpleRelayTestContainer, SuperSimpleRelayTest} from './components/SimpleRelayTest';

import ReactDOM from 'react-dom';
import {Router, Route} from 'react-router';
import BrowserHistory from 'react-router/lib/BrowserHistory';
import ReactRouterRelay from 'react-router-relay';

const ViewerQueries = {
  viewer: () => Relay.QL`query { viewer }`
};

ReactDOM.render((
    <Router history={new BrowserHistory()} createElement={ReactRouterRelay.createElement}>
      <Route path="/" component={App} >
        <Route path="test" component={SuperSimpleRelayTest} />
        <Route path="test/:userId" component={SimpleRelayTestContainer} queries={ViewerQueries} />
      </Route>
    </Router>
), document.getElementById('root'));

App.js:

import 'babel/polyfill';
import {Link} from 'react-router';

class App extends React.Component {
  render() {
    var userId = "VXNlcjoxMDI1";

    return (
        <div>
          <h1>App</h1>
          <ul>
            <li>
              <Link to="/test">test static</Link>
            </li>
            <li>
              <Link to={`/test/${userId}`}>test</Link>
            </li>
          </ul>
          {this.props.children}
        </div>
    );
  }
}

export default App;

SimpleRelayTest.js:

import 'babel/polyfill';

export class SuperSimpleRelayTest extends React.Component {
  render() {
    console.log("inside SuperSimpleRelayTest");          // this works. I see this message
    return (
      <h1>static component</h1>
    );
  }
}

class SimpleRelayTest extends React.Component {
  render() {
    logger.trace("inside Simplerelaytest");            // never see this message
    return (
      <div>
        <h1>User details</h1>
        <ul>
          <li>ID: {this.props.viewer.user.id}</li>
        </ul>
      </div>
    );
  }
}

export var SimpleRelayTestContainer = Relay.createContainer(SimpleRelayTest, {
  initialVariables: {
    userId: null
  },

  fragments: {
    viewer: () => Relay.QL`
      fragment on Viewer {
        user(id: $userId) {
          id
        }
      }
    `
  }
});

package.json:

    "react": "^0.14.0-beta3",
    "react-bootstrap": "^0.25.100-react-pre.0",
    "react-relay": "^0.2.1",
    "react-dom": "^0.14.0-beta3",
    "react-router": "^1.0.0-beta3",
    "react-router-relay": "^0.4.4",

url variables missing in rootQuery

I have a bug since I updated react-router-relay form 0.6.1 to 0.7.0

// This handles e.g. /widgets/3.
const widgetRoute = (
  <Route
    path="widgets/:widgetId" component={Widget}
    queries={WidgetQueries}
  />
);

3 do not pass as a variable to my server root query anymore

Accomodate multiple components in a route?

In the sidebar example, multiple components are specified for a route.
https://github.com/rackt/react-router/blob/master/examples/sidebar/app.js#L102

Should this be possible with relay? Would the queries parameter need to accept an object mapping queries to the multiple components? Like:

<Route components={{sidebar: Sidebar, content: Content}}
       queries={{sidebar: sidebarQueries, content: contentQueries}}>...</Route>

Or, perhaps the queries could be available for all the (relay) components.

<Route components={{sidebar: Sidebar, content: Content}}
       queries={routeQueries}>...</Route>

renderFailure on subroute

I renderFailure never gets called on a subroute. Say i have this:

<Route path="/" queries={rootqueries}>
  <Route path="/test" queries={testqueries} renderFailure={err => <div>Error</div>} />
</Route>

if testqueries fails with this response:

{
  data: { testdata: null },
  errors: [{ message: 'Test error', locations: [{ line: 1, column: 14}] }]
}

it thinks data fetched ok and tries to render normally.

this.props.children not rendered as expected

Hey, thanks for creating this library.
Recently, I tried a chat example using Relay using this library but seems no luck
I defined my routes as following
https://github.com/transedward/relay/blob/master/examples/chat/js/app.js

when go to '/', React will render ChatApp
https://github.com/transedward/relay/blob/master/examples/chat/js/components/ChatApp.js,
then ThreadSection would be rendered as expected, which is good,
then seems I add transition in ComponentWillMount to 'thread/' +currentId,
It should render MessageSection for this.props.children
But instead, console will throw Cannot set property 'validated' of undefined and nothing be rendered for this.props.children
I print this.props.children, and which is as normal React Component should look like
and props got correct query result

And I can't find working example for this lib, so I sure if I'm doing anything wrong.
If you can help, I will be grateful, clone, npm install and npm start, then looking at console
You can see what this.props.children looks like
thanks for any advice and feedback in advance

prepareVariables for root queries?

I'm trying to pass a variable to a root query, like this:

const ServiceQueries = {
  service: () => Relay.QL`query { service(id: $serviceId) }`,
};
// ...
<Route name="editService" path="/service/:serviceId" component={ EditServicePage } queries={ ServiceQueries } />

Problem is, my graphQL schema expects serviceId to be an Int, but react-router-relay is passing it as a String. is there any way to do something like prepareVariables for the root queries so that I can parseInt(serviceId, 10) before sending the query?

Creating an "Instantfetch" React Router Link component

@devknoll came up with what I think is a really cool idea on Slack the other day. I'll paste it in here as it's about to be eaten and dissolved forever by the dread Slack time monster.

kylemathews [3:57 PM]
I don’t like 500ms delays so I’d vote for pulling the detailed view fragment into the list view query to avoid that :simple_smile:

gmonaco [3:57 PM]
that gives me an idea: declarative http://instantclick.io for relay

kylemathews [3:58 PM]
@gmonaco: oooo nice! That’d be really cool!

gmonaco [3:58 PM]
^ declarative too! 😉

gmonaco [3:58 PM]
clicking this will take you to this route, here are the data requirements that route needs = done

kylemathews [3:59 PM]
yeah, just wrap React-Router’s ​Link​ component

...

kylemathews [4:00 PM]
actually, I’d use a instantfetch Link everywhere

kylemathews [4:00 PM]
why not really

gmonaco [4:02 PM]
couple it with some machine learning to see if they’re likely to actually click it, right?

gmonaco [4:02 PM]
…right? 😉

kylemathews [4:03 PM]
I think what instantclick is solid—measure how long they hover over a link e.g. past 200ms start fetching that route’s data (edited)

kylemathews [4:04 PM]
doesn’t Relay detect if a particular query is already in-flight and not send another one? Otherwise we’d need to prevent that.

gmonaco [4:05 PM]
it does in theory, but I think there might still be a bug with that

My question now is how to actually implement this? My thought is that the InstantLink component would detect mouseovers and once ~200miliseconds has gone by, trigger a fetch for that route's data. The component part is easy but I haven't figured out how to do the manual fetch. I did a quick look through the code here but wasn't able to figure out if there's a clear way to say "give me all Relay queries for this ReactRouter route".

Any thoughts @devknoll and @taion?

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.