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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@grafoo/react

v1.4.2

Published

grafoo client react bindings

Readme

@grafoo/react

Install

$ npm i @grafoo/{core,react} && npm i -D @grafoo/babel-plugin

Setup

Assuming you already have babel installed, the only additional step required to build an application with Grafoo is to configure @grafoo/babel-plugin. The options it accepts are idFields - the fields Grafoo will take to build unique identifiers, and schema, which is a relative path to your schema file.

{
  "plugins": [
    [
      "@grafoo/babel-plugin",
      {
        "schema": "schema.graphql",
        "idFields": ["id"]
      }
    ]
  ]
}

API

Provider

Provider receives a single client instance prop that will be consumed by the Consumer components.

import React from "react";
import createClient from "@grafoo/core";
import { Provider } from "@grafoo/react";

function fetchQuery(query, variables) {
  const init = {
    method: "POST",
    body: JSON.stringify({ query, variables }),
    headers: {
      "content-type": "application/json"
    }
  };

  return fetch("http://some.graphql.api", init).then(res => res.json());
}

const client = createClient(fetchQuery);

export default function App() {
  return (
    <Provider client={client}>
      <SomeComponent />
    </Provider>
  );
}

Consumer

Consumer is the component that performs query requests to your GraphQL API. It accepts the following props:

| Name | Type | Default | Required | Descrition | | --------- | -------- | ------- | -------- | ------------------------------------------------------------ | | query | object | - | false | a query created with @grafoo/core/tag | | variables | object | - | false | a GraphQL variables object for the query prop | | mutations | object | - | false | a map of mutations (description below) | | skip | boolean | false | false | whether Consumer should skip the query request initially | | children | function | - | false | a render function (description below) |

Render parameter

The Consumer render function takes as parameter an object with the following props:

| Name | type | Descrition | | ------- | -------- | ------------------------------------------------------------ | | client | object | the client instance | | load | function | a method to execute a query with the query prop | | loading | boolean | whether the client is executing a query or not | | loaded | boolean | whether the query data is already cached | | errors | string[] | an array of GraphQL errors from a failed request to your API |

The remaining props are:

  • the data fetched by the client and shaped according to your query
  • mutation functions generated by the mutations object prop

Example

import React from "react";
import gql from "@grafoo/core/tag";
import { Consumer } from "@grafoo/react";

const ALL_POSTS = gql`
  query getPosts($orderBy: PostOrderBy) {
    allPosts(orderBy: $orderBy) {
      title
      content
      createdAt
      updatedAt
    }
  }
`;

export default function Posts() {
  return (
    <Consumer query={ALL_POSTS} variables={{ orderBy: "createdAt_DESC" }}>
      {({ client, load, loaded, loading, errors, allPosts }) => (
        <h1>
          <marquee>👆 do whatever you want with the variables above 👆</marquee>
        </h1>
      )}
    </Consumer>
  );
}

Mutations

The mutations prop is a map of mutation objects that are shaped like so:

const createPost = {
  query: CREATE_POST_MUTATION,
  optimisticUpdate: ({ allPosts }, variables) => ({
    allPosts: [{ ...variables.postInput, id: "tempID" }, ...allPosts]
  }),
  update: ({ allPosts }, response) => ({
    allPosts: allPosts.map(p => (p.id === "tempID" ? response.post : p))
  })
};

const mutations = { createPost };

A mutation object receives the following props:

| Name | Type | Required | Descrition | | ---------------- | -------- | -------- | ------------------------------------------------------------------- | | query | object | true | a mutation query created with @grafoo/core/tag | | update | function | false | updates the cache when a request is completed (description below) | | optimisticUpdate | function | false | updates the cache before a request is completed (description below) |

Each mutation will generate a single function that accepts a GraphQL variables object as argument and return a promise that will resolve with the mutation data or reject with GraphQL errors.

type MutationFn = (variables: Variables) => Promise<MutationResponse>;

Mutation query dependency

Important to notice that to update the cache update and optimistUpdate hooks depend on a query and it's variables object props (so they need be declared in the Consumer). If you need to perform a mutation but updating the cache is not strictly important you can just use the mutation promise resolved data or use the client instance directly.

update

type UpdateFn = (query: QueryData, response: MutationResponse) => CacheUpdate;

The mutation update function is resposible to update the cache when the request is completed. It receives as paremeters an object containing the data from the query it depends upon and the mutation response sent by the server. update return type is an object that describes the changes to be made to the cache.

optimisticUpdate

type OptimistcUpdateFn = (query: QueryData, variables: Variables) => CacheUpdate;

In modern UIs it's to be expected that every user interaction occur in a fraction of seconds. optimisticUpdate responsability is to skip the mutation network roundtrip and update the cache instantaneously, making sure such interactions are as fast as they can be. optimisticUpdate as in update takes as first paremater the depedent query data. As second paremater it receives the variables object with which it's correpondent generated mutation function was called. And again it should return an object that describes the changes to be made to cache.

If you want to perform an optimitic update you have to make sure that the data you are inserting contains the field or fields to extract a unique identifier. For instance, say @grafoo/babel-plugin idFields option is set to insert a property id. Is to be expected that your update has that field mocked.

Example

import React from "react";
import gql from "@grafoo/core/tag";
import { Consumer } from "@grafoo/react";

const ALL_POSTS = gql`
  query getPosts($orderBy: PostOrderBy) {
    allPosts(orderBy: $orderBy) {
      title
      content
      createdAt
      updatedAt
    }
  }
`;

const CREATE_POST = gql`
  mutation createPost($content: String, $title: String, $authorId: ID) {
    createPost(content: $content, title: $title, authorId: $authorId) {
      title
      content
      createdAt
      updatedAt
    }
  }
`;

const mutations = {
  createPost: {
    query: CREATE_POST,
    optimisticUpdate: ({ allPosts }, variables) => ({
      allPosts: [{ ...variables, id: "tempID" }, ...allPosts]
    }),
    update: ({ allPosts }, data) => ({
      allPosts: allPosts.map(p => (p.id === "tempID" ? data.createPost : p))
    })
  }
};

const submit = mutate => event => {
  event.preventDefault();

  const { title, content } = event.target.elements;

  mutate({ title: title.value, content: content.value }).then(mutationData => {
    console.log(mutationData);
  });
};

export default function PostForm() {
  return (
    <Consumer query={ALL_POSTS} mutations={mutations}>
      {({ createPost }) => (
        <form onSubmit={submit(createPost)}>
          <input name="title" />
          <textarea name="content" />
          <button>submit</button>
        </form>
      )}
    </Consumer>
  );
}

LICENSE

MIT