npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-apollo-graphql

v1.0.2

Published

Get rid of decorators and use Apollo GraphQL queries and mutations in the simple and readable way.

Downloads

43

Readme

react-apollo-graphql

npm CircleCI

gzip size size module formats: umd, cjs, esm

This is opinionated replacement for graphql decorator from react-apollo package.

Under development, API can change

npm install --save react-apollo-graphql

It provides:

  • simple error handling on the component level
  • readable passing of queries' results to your component
  • typed props and render props using flow type
  • server side render

Usage

Using <ApolloProvider /> from react-apollo

import GraphQL from 'react-apollo-graphql';
import ApolloClient from 'apollo-client';
import ApolloProvider from 'react-apollo';

<ApolloProvider client={new ApolloClient(...)}>
  <GraphQL render={(queries, mutations, props) => <div />} />
</ApolloProvider>

Passing ApolloClient directly

import GraphQL from 'react-apollo-graphql';
import ApolloClient from 'apollo-client';

<GraphQL client={new ApolloClient(...)} render={(queries, mutations, props) => <div />} />

Queries

In order to use define and use queries, one has to initialize them.

// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, ObservableQuery } from 'react-apollo-graphql/lib/types';

const queries = {
  // queryA will be resolved only once
  queryA: (
    client: ApolloClient,
    props: Object
  ): ObservableQuery<{ id: number }> => client.watchQuery({
    query: gql`{ id }`,
  }),
  // queryB will be resolved everytime the relevant props change
  queryB: (
    client: ApolloClient,
    props: Object,
    options: QueryInitializerOptions
  ): ObservableQuery<{ name: string }> => {
    // add our function which will be called on every props change
    options.hasVariablesChanged((currentProps, nextProps) => {
      if (currentProps.name === nextProps.name) {
        return false;
      }

      return { name: nextProps.name };
    });

    return client.watchQuery({
      query: gql`query test($name: String!) { id(name: $name)}`,
      variables: { name: props.name },
    });
  }
};

<GraphQL
  queries={queries}
  render={(initializedQueries) => {
    console.log(initializeQueries.queryA.data);
    console.log(initializeQueries.queryA.loading);
    console.log(initializeQueries.queryA.error);
    console.log(initializeQueries.queryA.networkStatus);
    console.log(initializeQueries.queryA.partial);
  }}
/>

Mutations

In order to define and use mutations, one has to provided initializers. Initializers are called on every render so you have current props available in the initializers.

// @flow

import type { ApolloClient, QueryResult } from 'react-apollo-graphql/lib/types';

const mutations = {
  registerUser: (
    client: ApolloClient,
    props: Object
  ) => (): Promise<QueryResult<{ registerUser: boolean }>> => client.mutate({
    mutation: gql`mutation registerUser($email: String!) { registerUser(email: $email) }`,
    variables: {
      email: props.email,
    },
  }),
};

<GraphQL
  email="[email protected]"
  mutations={mutations}
  render={(queries, mutations, fetchers, props) => {
    mutations.registerUser(props.email).then(
      (data) => console.log(data.registerUser),
      e => console.error(e),
    );
  }}
/>

Fetchers

In order to use fetchers (queries that run only when user invokes them), user has to first initialize them. Fetchers are initialized with client and current props on each render and passed to render() function.

// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, QueryResult } from 'react-apollo-graphql/lib/types';

const fetchers = {
  // queryA will be resolved only once
  search: (
    client: ApolloClient,
    props: Object
  ) => (term: string): Promise<QueryResult<Array<{ id: number }>>> => client.query({
    query: gql`query search($term: String!) { search(term: $term) { id } }`,
    variables: { term },
  }),
};

<GraphQL
  fetchers={fetchers}
  text="text"
  render={(queries, mutations, fetchers, props) => {
    fetchers.search(props.text).then(
      (data) => console.log(data.search[0].id);
    );
  }}
/>

Fragments

In order to use fragments (you can simulate partial results using fragments), user has to first initialize them. Fragments are initialized with client, previous props and current props on componentWillMount and every update if props used by given fragment have changed. If props have not changed and you don't want fragment to fetch data on every update, return false.

// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, FragmentResult } from 'react-apollo-graphql/lib/types';

const fragments = {
  // user detail will be resolved on componentWillMount and on every update if props
  // used as variables have changed
  userDetail: (
    client: ApolloClient,
    previousProps: ?Object,
    currentProps: Object
  ): FragmentResult<{ __typename: 'User', id: number, name: string }> => {
    if (previousProps && previousProps.id === currentProps.id) {
      return false;
    }

    return client.readFragment({
      id: `User:${currentProps.id}`,
      fragment: gql`fragment userDetails on User { __typename, id, name }`,
    });
  }
};

<GraphQL
  fragments={fragments}
  id={10}
  render={(queries, mutations, fetchers, fragments, props) => {
    expect(fragments.userDetail).toEqual({
      __typename: 'User',
      id: 10,
      name: 'Fero',
    });
  }}
/>

Server side render

For server side rendering you need to:

  1. import helper as import { getDataFromTree } from 'react-apollo-graphql';
  2. instantiate your view (const view = <App />;)
  3. wait for all queries to be resolved await getDataFromTree(view);
  4. render view ReactDOM.renderToString(view);
  5. profit (but you have to hydrate your apollo store on the client side 😉 )

React-Router v4

// example taken from react-router v4 docs
import { createServer } from 'http';
import ApolloClient from 'apollo-client';
import ApolloProvider from 'react-apollo';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router';
import { getDataFromTree } from 'react-apollo-graphql';
import App from './App';

createServer(async (req, res) => {
  const context = {};
  const client = new ApolloClient();

  const view = (
    <StaticRouter
      location={req.url}
      context={context}
    >
      <ApolloProvider client={client}>
        <App/>
      </ApolloProvider>
    </StaticRouter>
  );

  await getDataFromTree(view);

  const html = ReactDOMServer.renderToString(view);

  if (context.url) {
    res.writeHead(301, {
      Location: context.url
    });
    res.end();
  } else {
    res.write(`
      <!doctype html>
      <div id="app">${html}</div>
    `);
    res.end();
  }
}).listen(3000);

API

ApolloClient

  • apollo client is provided from apollo-client package. See documentation.

QueryInitializerOptions

// @flow

export type QueryInitializerOptions = {
  // sets function to determine if there is a relevant change in props to compute new variables
  // returns false if there is no change in props used for variables
  // or returns new variables for query.setVariables()
  hasVariablesChanged: (
    (currentProps: Object, nextProps: Object) => boolean | { [key: string]: any },
  ) => void,
};

<GraphQL fetchers?={Fetchers} fragments?={Fragments} queries?={Queries} mutations?={Mutations} render={RenderFunction} />

  • Fragments = { [key: string]: (client: ApolloClient, previousProps: ?Object, currentProps: Object) => FragmentResult<any> }
    • optional prop, object with fragments' initializer
    • each initializer will be initialized with Apollo client, previousProps and props passed to the initializer on each mount and update
    • each initializer is update only if it does not return false
    • each initializer has to return false or result of client.readFragment() (this means that it has to call the client.readFragment() method)
  • Fetchers = { [key: string]: (client: ApolloClient, props: Object) => (...args: any) => Promise<QueryResult<*>>}
    • optional prop, object with fetchers' initializer
    • each initializer will be initialized with apollo client and props passed to the initializer on each render (on mount and every update)
    • each initializer has to return (...args: any) => Promise<QueryResult<*>> (this means that it has to call the client.query() method)
  • Queries = { [key: string]: (client: ApolloClient, props: Object, options: QueryInitializerOptions) => ObservableQuery<*> }
    • optional prop, object with query initializers.
    • each initializer will be initialized with apollo client and props passed to initializer on component mount
    • each initializer has to return ObservableQuery (this means that it has to call the client.watchQuery() method)
  • Mutations = { [key: string]: (client: ApolloClient, props: Object) => () => Promise<QueryResult<*>>}
    • optional prop, object with mutation initializers
    • each initializer will be initialized with apollo client and props passed to the initializer on each render (on mount and every update)
    • each initializer has to return () => Promise<QueryResult<*>> (this means that it has to call the client.mutate() method)
  • RenderFunction = (queries: InitializedQueries, mutations: InitializedMutations, fetchers: InitializedFetchers, props: Object) => React$Element<any>
    • called on mount and updates
    • queries arg: result of each query initializer passed to the queries prop on <GraphQL /> component will be mapped to it's result, plus additional methods like fetchMore(), refetch(), etc see client.watchQuery() method
    • mutations arg: each mutation initializer from the mutations prop passed to the <GraphQL /> component will be called on render and the result will be passed under the same key to the mutations arg of render function.
    • fetchers arg: each fetcher initializer from the fetchers prop passed to the <GraphQL /> component will be called on render and the returned function will be passed under the same key to the fetchers arg of render function.
    • props arg: current props passed to <GraphQL /> component