0.0.38 • Published 2 years ago

use-pq v0.0.38

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

npm npm bundle size Codecov GitHub license

About The Project

usePq aims to make GraphQL consumption truly only-what-you-need. GraphQL is a great tool for data fetching without bombarding an API with chained requests or building a local replica. The one thing that annoys me about GraphQL is writing query files. These need to be tweaked, formatted, and in some cases compiled and shipped as utilities.

Building a query per page seems to me like patching RESTful thinking into GraphQL. The idea behind usePq is to do with query declarations and instead use observables to build the exact query that you need, just as GraphQL was meant to be used.

To view the full documentation, go to use-pq.com.

Package Details

Getting Started

usePq is not invasive: you can use as little or as much as you want. You only need to provide a fetching function and start querying away.

Prerequisites

usePq has a peer dependency on React 17+.

Installation

pnpm add use-pq

Basic Usage

In a component, call usePq and provide a query handler. You can then request fields to fetch by accessing keys from p.

import { usePq } from 'use-pq'
import client from 'api'

export function UserStatus() {
  const [p, q, { isLoading }] = usePq(async (query) =>
    // Use any GraphQL client that can post a string query
    // and set the response.
    client.query(query).then(({ data }) => data)
  )

  // Access and build any reference from p
  const user = p.session.user

  const displayName = `${user.title} ${user.name}`

  return (
    <div>
      {isLoading ? (
        <span>loading...</span>
      ) : (
        <span>
          {displayName}: {user.status}
        </span>
      )}
    </div>
  )
}

API

const [p, q, { isLoading, commitQuery }] = usePq(handler)

Arguments

handler: usePq takes a single argument, which is a function with two arguments: the query and a data setter.

The data setter must be called asynchronously.

Return value

p: A Proxy that captures the values that are needed during the first render phase.

q: The query that was built from the render phase from accessing properties. If the field was accessed by React during rendering, it will also be registered.

isLoading: The state of the field capture phase. This is true while a query does not exist and the client has not set any data.

commitQuery: A function that can be called to manually trigger a commit of captured changes to the query.

Feature Usage

Lists of fields

Lists of fields cannot be access by themselves. It is possible to declare a field as a field list by accessing .listOf('fieldName') on the parent.

export function Users() {
  const [p, q, { isLoading }] = usePq(handler)

  // users is a plain array
  const users = p.listOf('users')

  // On the first render, pq will capture the values
  // used by the component by with list having a
  // single proxy entry i.e. list = [proxy]
  return (
    <div>
      {isLoading ? (
        // This will be the first node to be painted
        // to the browser, since pq will trigger a rerender
        // with isLoading = true after capturing
        <span>loading...</span>
      ) : (
        // React will attempt to render name by checking if
        // if it a valid element. When an object is being verified,
        // React checks if it has Symbol.iterator. When the iterator
        // property is accessed, the field is registered for the query.
        users.map(({ name }) => <span>{name}</span>)
      )}
    </div>
  )
}

Fields with arguments

Fields with arguments can be declared by accessing a key in bracket notation.

export function User({ id }) {
  const [p, q, { isLoading }] = usePq(handler)

  // Proxies can destructure properties
  const { id, name } = p[`user(id: ${id})`]

  return (
    <div>
      {isLoading ? (
        <span>loading...</span>
      ) : (
        <span>
          {id}: {name}
        </span>
      )}
    </div>
  )
}

Field lists with arguments

Combining the argument and list notation will return an array.

export function UsersLimit({ limit }) {
  const [p, q, { isLoading }] = usePq(handler)

  const users = p
    .listOf(`users(limit: ${limit})`)
    .map(({ id, name }) => `${id}: ${name}`)

  return (
    <div>
      {isLoading ? (
        <span>loading...</span>
      ) : (
        users.map((entry) => <span>{entry}</span>)
      )}
    </div>
  )
}

Arguments as a function

It is possible to provide arguments without string interpolation by using a $ prefix after the field that needs arguments.

export function User({ id }) {
  const [p, q, { isLoading }] = usePq(handler)

  // The function name can be anything
  // as long as the first character is $
  const { id, name } = p.user.$({ id })

  return (
    <div>
      {isLoading ? (
        <span>loading...</span>
      ) : (
        <span>
          {id}: {name}
        </span>
      )}
    </div>
  )
}

Fields with arguments as functions

Arguments as function can also be used for list fields.

export function Users() {
  const [p, q, { isLoading }] = usePq(handler)

  // The function name can be anything
  // as long as the first character is $
  const users = p
    .listOf('users')
    .$({ limit })
    .map(({ id, name }) => `${id}: ${name}`)

  return (
    <div>
      {isLoading ? (
        <span>loading...</span>
      ) : (
        users.map((entry) => <span>{entry}</span>)
      )}
    </div>
  )
}

Committing captured changes in children

usePq relies on rerendering cycles to capture and commit fields that were accessed. If usePq is call in a parent component and passed down as a prop to a child, rerendering the child and not the parent will not trigger the commit. Invoking commitQuery does this job.

function App() {
  const [{ user }, q, { commitQuery, isLoading }] = usePq(handler)

  return <User user={user} commitQuery={commitQuery} />
}

export function User({ user, commitQuery }) {
  const [id, setId] = useState(1)

  // When id changes, the query will be updated after
  // rendering.
  useEffect(commitQuery, [id])

  return (
    <div>
      {isLoading ? <span>loading...</span> : <span>{user.$({ id }).name}</span>}
      <button onClick={() => setId(i + 1)}>next</button>
    </div>
  )
}

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feat/title)
  3. Commit your Changes (git commit -m 'Add feature')
  4. Push to the Branch (git push origin feat/title)
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE.txt for more information.

Contact

Project Link: https://github.com/ofrbg/use-pq

0.0.38

2 years ago

0.0.37

2 years ago

0.0.36

2 years ago

0.0.35

2 years ago

0.0.33

2 years ago

0.0.32

2 years ago

0.0.31

2 years ago

0.0.30

2 years ago

0.0.29

2 years ago

0.0.28

2 years ago

0.0.27

2 years ago

0.0.26

2 years ago

0.0.24

2 years ago

0.0.22

2 years ago

0.0.21

2 years ago

0.0.20

2 years ago

0.0.19

2 years ago

0.0.18

2 years ago

0.0.17

2 years ago

0.0.15

2 years ago

0.0.14

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago