Skip to content

Instantly share code, notes, and snippets.

@janicduplessis
Created October 3, 2016 18:49
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save janicduplessis/f513032eb37cdde5d050d9ce8cf0b92a to your computer and use it in GitHub Desktop.
Save janicduplessis/f513032eb37cdde5d050d9ce8cf0b92a to your computer and use it in GitHub Desktop.
// @flow
import React from 'react';
import {
ActivityIndicator,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Relay from 'react-relay';
type Variables = {[name: string]: mixed};
type Config = {
queries: {[name: string]: any};
queriesParams?: ?(props: Object) => Object;
fragments: {[name: string]: any};
initialVariables?: Variables;
prepareVariables?: (prevVariables: Variables, route: any) => Variables;
forceFetch?: bool;
onReadyStateChange?: (readyState: any) => void;
renderFetched?: (
props: Object,
fetchState: { done: bool; stale: bool }
) => ?ReactElement<any>;
renderLoading?: () => ?ReactElement<any>;
renderFailure?: (error: Error, retry: ?() => void) => ?ReactElement<any>;
};
// TODO: Make proper loading / failure components.
const LoadingView = () => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator size="large" />
</View>
);
const FailureView = ({ error, retry }) => (
<View style={{ paddingTop: 46, alignItems: 'center' }}>
<Text>Error</Text>
<Text>{error.message}</Text>
<TouchableOpacity onPress={retry}>
<Text>Try again</Text>
</TouchableOpacity>
</View>
);
export function createRenderer(
Component: ReactClass<*>,
config: Config
): ReactClass<*> {
return createRendererInternal(Component, {
forceFetch: true,
renderLoading: () => <LoadingView />,
renderFailure: (error, retry) => <FailureView error={error} retry={retry} />,
...config,
});
}
function createRendererInternal(
Component: ReactClass<*>,
config: Config
): ReactClass<*> {
const {
queries,
queriesParams,
forceFetch,
renderFetched,
renderLoading,
renderFailure,
onReadyStateChange,
fragments,
initialVariables,
prepareVariables,
} = config;
const RelayComponent = Relay.createContainer(Component, {
fragments,
initialVariables,
prepareVariables,
});
class RelayRendererWrapper extends React.Component {
state = {
queryConfig: this._computeQueryConfig(this.props),
};
render() {
return (
<Relay.Renderer
Container={RelayComponent}
forceFetch={forceFetch || false}
onReadyStateChange={onReadyStateChange}
queryConfig={this.state.queryConfig}
environment={Relay.Store}
render={({ done, error, props, retry, stale }) => {
if (error) {
if (renderFailure) {
return renderFailure(error, retry);
}
} else if (props) {
if (renderFetched) {
return renderFetched({ ...this.props, ...props }, { done, stale });
} else {
return <RelayComponent {...this.props} {...props} />;
}
} else if (renderLoading) {
return renderLoading();
}
return undefined;
}}
/>
);
}
componentWillReceiveProps(nextProps: Object) {
if (this.props.routeParams !== nextProps.routeParams) {
this.setState({
queryConfig: this._computeQueryConfig(nextProps),
});
}
}
_computeQueryConfig(props: Object) {
const params = queriesParams ? queriesParams(props) : {};
const queryConfig = {
name: `relay-route-${RelayComponent.displayName}`,
queries: { ...queries },
params,
};
return queryConfig;
}
}
return RelayRendererWrapper;
}
@gre
Copy link

gre commented Oct 20, 2016

if I may add a suggestion, I think you should not have fragments / initialVariables / prepareVariables but instead the Component should already be a RelayComponent.
It simplifies your code too and it makes things more uncoupled.

class Screen extends Component{}
const Container = Relay.createContainer(Screen, { fragments });
const Final = RelayUtils.createRenderer(Container, { queries, ... });

it's also more close to https://github.com/relay-tools/react-router-relay

@sibelius
Copy link

sibelius commented Oct 20, 2016

You should avoid the use of dash here: relay-route-${RelayComponent.displayName} for the name of QueryConfig

it should be

const name  = `relay_route_${RelayComponent.displayName}`.replace(/[\(|\)]/g, '_');

parenthesis () and dash - can cause some problems in Graphql

for instance:

Syntax Error GraphQL request (1:25) Expected $, found Name "Search"

1: query relay_route_Relay(Search)($id_0:ID!) {
                           ^
2:   node(id:$id_0) {

[ { line: 1, column: 25 } ]
GraphQLError: Syntax Error GraphQL request (1:25) Expected $, found Name "Search"

1: query relay_route_Relay(Search)($id_0:ID!) {
                           ^
2:   node(id:$id_0) {

take a look on this: r0b1n/rnrf-relay-renderer#10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment