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-graphql-client

v0.1.8

Published

The library gives you a simple GraphQL client for React applications. But it shouldn't be used for production. Rather it should be used as inspiration for you and others to contribute to the GraphQL ecosystem.

Downloads

23

Readme

react-graphql-client

The library gives you a simple GraphQL client for React applications. But it shouldn't be used for production. Rather it should be used as inspiration for you and others to contribute to the GraphQL ecosystem.

The library hasn't powerful features. There is no caching, normalization or global state. But it works and it should show you that it's not too difficult to start out with the implementation of a simple GraphQL client library. You can look into the source code (src/) and the example application (example/) to see that there is not too much to it.

If you feel the urge to build a sophisticated GraphQL client library (for React) on top of it, please do it! I encourage everyone to contribute to this ecosystem, because I feel there should be more players in this field. I would love to see this library and repository as inspiration for you and others to contribute to the GraphQL ecosystem.

Installation

On the command line, install it with npm: npm install --save react-graphql-client

Setup

In your top level React component, initialize the GraphQL client with a GraphQL endpoint and pass it to the provided Provider component from the library.

import React from 'react';
import ReactDOM from 'react-dom';

import GraphQLClient, { Provider } from 'react-graphql-client';

import App from './App';

const client = new GraphQLClient({
  baseURL: 'https://mydomain.com/graphql',
});

ReactDOM.render(
  <Provider value={client}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

That's it. The GraphQL client is accessible in every React component due to React's Context API.

Query

In order to execute a GraphQL query operation, use the Query component that is provided from the library. The Query component implements the render prop pattern with its child as a function specification. In the child as a function, you have access to the result of the query operation and further information such as loading state and errors.

import React from 'react';

import { Query } from 'react-graphql-client';

const GET_ORGANIZATION = `
  query (
    $organizationLogin: String!
  ) {
    organization(login: $organizationLogin) {
      name
      url
    }
  }
`;

const App = () =>
  <Query
    query={GET_ORGANIZATION}
    variables={{
      organizationLogin: 'the-road-to-learn-react',
    }}
  >
    {({ data, loading, errors }) => {
      if (!data) {
        return <p>No information yet ...</p>;
      }

      const { organization } = data;

      if (loading) {
        return <p>Loading ...</p>;
      }

      if (errors) {
        return (
          <p>
            <strong>Something went wrong:</strong>
            {errors.map(error => error.message).join(' ')}
          </p>
        );
      }

      return (
        <Organization organization={organization} />
      );
    }}
  </Query>

The query executes when it is rendered. The query and optional variables are passed as props to the Query component. Every time one of those props changes, the query will execute again.

Query with Pagination

In order to query a paginated list of items, you need to pass in sufficient variables to your query. This is specific to your GraphQL API and not to the library. However, after querying more items (e.g. with a "More"-button), there needs to be a resolver function to merge the previous with the new result.

import React from 'react';

import { Query } from 'react-graphql-client';

const GET_REPOSITORIES_OF_ORGANIZATION = `
  query (
    $organizationLogin: String!,
    $cursor: String
  ) {
    organization(login: $organizationLogin) {
      name
      url
      repositories(first: 5, after: $cursor) {
        totalCount
        pageInfo {
          endCursor
          hasNextPage
        }
        edges {
          node {
            id
            name
            url
            watchers {
              totalCount
            }
            viewerSubscription
          }
        }
      }
    }
  }
`;

const resolveFetchMore = (data, state) => {
  const { edges: oldR } = state.data.organization.repositories;
  const { edges: newR } = data.organization.repositories;

  const updatedRepositories = [...oldR, ...newR];

  return {
    organization: {
      ...data.organization,
      repositories: {
        ...data.organization.repositories,
        edges: updatedRepositories,
      },
    },
  };
};

const App = () =>
  <Query
    query={GET_REPOSITORIES_OF_ORGANIZATION}
    variables={{
      organizationLogin,
    }}
    resolveFetchMore={resolveFetchMore}
  >
    {({ data, loading, errors, fetchMore }) => {
      ...

      return (
        <Organization
          organization={organization}
          onFetchMoreRepositories={() =>
            fetchMore({
              query: GET_REPOSITORIES_OF_ORGANIZATION,
              variables: {
                organizationLogin,
                cursor:
                  organization.repositories.pageInfo.endCursor,
              },
            })
          }
        />
      );
    }}
  </Query>

const Organization = ({ organization, onFetchMoreRepositories }) => (
  <div>
    <h1>
      <a href={organization.url}>{organization.name}</a>
    </h1>
    <Repositories
      repositories={organization.repositories}
      onFetchMoreRepositories={onFetchMoreRepositories}
    />

    {organization.repositories.pageInfo.hasNextPage && (
      <button onClick={onFetchMoreRepositories}>More</button>
    )}
  </div>
);

After a click on the "More"-button, the results of both lists of repositories should be merged.

Mutation

Last but not least, there is a Mutation component analog to the Query component which is used to execute a mutation. However, in contrast to the Quert component, the Mutation component doesn't execute the mutation on render. You get an explicit callback function in the render prop child function for it.

import React from 'react';

import { Query, Mutation } from 'react-graphql-client';

...

const WATCH_REPOSITORY = `
  mutation($id: ID!, $viewerSubscription: SubscriptionState!) {
    updateSubscription(
      input: { state: $viewerSubscription, subscribableId: $id }
    ) {
      subscribable {
        id
        viewerSubscription
      }
    }
  }
`;

const resolveWatchMutation = (data, state) => {
  const { totalCount } = state.data.updateSubscription.subscribable;
  const { viewerSubscription } = data.updateSubscription.subscribable;

  return {
    updateSubscription: {
      subscribable: {
        viewerSubscription,
        totalCount:
          viewerSubscription === 'SUBSCRIBED'
            ? totalCount + 1
            : totalCount - 1,
      },
    },
  };
};

const Repositories = ({ repositories }) => (
  <ul>
    {repositories.edges.map(repository => (
      <li key={repository.node.id}>
        <a href={repository.node.url}>{repository.node.name}</a>

        <Mutation
          mutation={WATCH_REPOSITORY}
          initial={{
            updateSubscription: {
              subscribable: {
                viewerSubscription:
                  repository.node.viewerSubscription,
                totalCount: repository.node.watchers.totalCount,
              },
            },
          }}
          resolveMutation={resolveWatchMutation}
        >
          {(toggleWatch, { data, loading, errors }) => (
            <button
              type="button"
              onClick={() =>
                toggleWatch({
                  variables: {
                    id: repository.node.id,
                    viewerSubscription: isWatch(
                      data.updateSubscription,
                    )
                      ? 'UNSUBSCRIBED'
                      : 'SUBSCRIBED',
                  },
                })
              }
            >
              {data.updateSubscription.subscribable.totalCount}
              {isWatch(data.updateSubscription)
                ? ' Unwatch'
                : ' Watch'}
            </button>
          )}
        </Mutation>
      </li>
    ))}
  </ul>
);

Within the Mutation component the data object should be used to render relevant information. This data can be set with an initial value by using the initial prop on the Mutation component. Furthermore, after executing a mutation, a resolveMutation function as a prop can be provided to deal with the previous state and the mutation result. In the previous case, the new totalCount wasn't provided by the GraphQL API. So you can do it by yourself with this resolver function.

Contribute

As mentioned, if you are curious, checkout the examples/ folder to get a minimal working application. You need to fulfil the following installation instructions for it:

In addition, checkout the src/ folder to see that there is not much to implement for a simple GraphQL client. I hope this helps you to build your own library on top of it by forking this repository.

Otherwise, feel free to improve this repository and to fix bugs for it. However, I wouldn't want to grow it into a powerful GraphQL client library. Rather I would love to see this library and repository as inspiration for you and others to contribute to this new GraphQL ecosystem.

Want to learn more about React + GraphQL + Apollo?