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-blips

v0.14.1

Published

Official React bindings for Blips

Downloads

45

Readme

React-Blips

Official React bindings for Blips

build status npm version npm downloads

Table of contents:

Installation

npm i react-blips

This assumes you are using npm and have already installed blips and graphql

Usage

The Provider instance

To get started you'll have to create the blips client and pass it as a prop to the Provider component

import { BlipsClient } from 'blips'
import { Provider } from 'react-blips'
// ...

const client = new BlipsClient(...)

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

Connect components with graphql

graphql is the function that creates container components which are connected to the store.

graphql(...operations [, config])(BaseComponent)

// ...
import { graphql } from 'react-blips'

const TodoList = ({ data: { allTodos = [] } }) => (
  <ul className="todo-list">
    {allTodos.map(todo => (
      <li key={todo.id}>
        <label>
          <input type="checkbox" checked={todo.completed} />
          {todo.label}
        </label>
      </li>
    ))}
  </ul>
)

const allTodosQuery = `
  query allTodosQuery {
    allTodos {
      id
      label
      completed
    }
  }
`

export default graphql(allTodosQuery)(TodoList)

You can add as many operations as you need, the only requirement is that if you provide a config object, it must be the last argument.

// ...
const config = { ... }

export default graphql(allTodosQuery, allUsersQuery, config)(TodoList)

By default, the result of your queries will be added to the component's props under the data key:

props: {
  data: {
    allTodos: [...],
    allUsers: [...]
  }
}

The data prop

In addition to the fields you query, the data prop may also contain a loading flag and/or an errors object

loading

Indicates whether or not the operations are still executing.

props: {
  data: {
    loading: true
  }
}

While loading, the fields you've queried may not be part of the data. Make sure to add some default values or display a loader until the operations finish executing.

// default values:

const TodoList = ({ data: { allTodos = [] } }) => (
  <ul className="todo-list">{allTodos.map(todo => <li>{todo}</li>)}</ul>
)

// displaying loader while operations is executing

const TodoList = ({ data: { loading, allTodos } }) => {
  return loading ? (
    <LoadingSpinner />
  ) : (
    <ul className="todo-list">{allTodos.map(todo => <li>{todo}</li>)}</ul>
  )
}
error

Contains all the errors that might have resulted from any of the operations.

props: {
  data: {
    loading: false,
    allTodos: [...],
    errors: {
      allUsers: { ... }
    }
  }
}

You should always have some sort of error-handling in place, this is generally a good practice, not only when using Blips.

The config object

graphql accepts an optional argument which represents the configuration object. This argument should always be provided last, after all operations.

graphql(...operations, config)(BaseComponent)
config.name

This property allows you to change the name of the data prop that gets passed down to your component.

graphql(...operations, { name: 'state' })(BaseComponent)

// props :{
//   ...
//   state: {
//     loading: false,
//     allTodos: [...],
//     allUsers: [...]
//   }
// }

You can also define config.name as a plain object if you wish to provide custom names for the other props (queries, mutations) that graphql adds to the container component

graphql(...operations, {
  name: {
    data: 'state',
    queries: 'geters',
    mutations: 'setters',
  },
})(BaseComponent)

// props :{
//   ...
//   state: { ... },
//   getters: { ... },
//   setters: { ... }
// }
config.options

This property is an object or function that allows you to provide the variables needed for your operations or to extend the context of the resolvers they will call.

// resolvers.js
const resolvers = {
  // ...
  Query: {
    // returns only todos of the logged user
    allTodosQuery: (obj, { first }, { store, user }) => {
      store.get('todos', { user_id: user.id }).slice(0, first)
    },
  },
}

// TodoList.js
const TodoList = ({ data: { allTodos = [] } }) => (
  <ul className="todo-list">{allTodos.map(todo => <li>{todo}</li>)}</ul>
)

const allTodosQuery = `
  query allTodosQuery($first: Int) {
    allTodos(first: $first) {
      id
      label
      completed
    }
  }
`

export default graphql(allTodosQuery, {
  options: {
    variables: { first: 10 },
    context: { user: localStorage.getItem('currentUser') },
  },
})

You can define config.options as a plain object, or as a function that takes the component’s props as an argument and returns the object.

export default graphql(allTodosQuery, {
  options: props => ({
    variables: { first: props.count },
    context: { user: props.user },
  }),
})

The queries prop

In addition to data, the connected component will also receive a queries prop which contains all the query operations specified, as methods that you can call manually.

// ...

onUserSelect = async id => {
  const { queries: { allTodosQuery, user }, count, user } = this.props
  const newUser = await user({ variables: { id } })
  const data = await allTodosQuery({ variables: { first: count }, context: { user: newUser } })

  this.setState({
    todos: data.allTodos,
  })
}

// ...

render() {
  return (
    <div>
      <select onChange={e => this.onUserSelect(e.target.value)}>
        <option value="600cba27-e8fe-413b-96bc-a9cbf6a2c897">John Doe</option>
        <option value="203d7785-14c4-4fa9-8436-b49351f5b6e5">Jane Doe</option>
      </select>
      <TodoList todos={this.state.todos} />
    </div>
  )
}

This is great for the above type of behaviour, but you can also use these queries to poll the state at a specific interval and update the component's state. The problem with this approach is that you'd have to add the props you want to poll for to the state so that your component updates correctly. If this is what you want, you're better off using subscription instead of query

The mutations prop

Another prop passed down to the container component is mutations. This prop contains all the mutations you provide to graphql()

class Todos extends Component {
  // ...

  onKeyUp = e => {
    const {
      mutations: { createTodoMutation = () => {} },
      data: { allTodos = [] },
    } = this.props

    createTodoMutation({ variables: { label: e.target.value } })
    // adds the new todo to the store and our subscription will handle the component update
  }

  // ...

  render() {
    const { data: { allTodos = [] } } = this.props

    return (
      <div>
        <input type="text" onKeyUp={this.onKeyUp} />
        <TodoList todos={allTodos} />
      </div>
    )
  }
}

const createTodoMutation = `
  mutation createTodoMutation(id: String, label: String!, completed: Boolean) {
    createTodo {
      id
      label
      completed
    }
  }
`

export default graphql(allTodosSubscription, createTodoMutation, {
  options: {
    context: { user: localStorage.getItem('currentUser') },
  },
})

Subscribing to state changes

If you provide subscription operations to graphql(), the connected component will also subscribe to state changes and will update correctly. It will also clean up after itself when unmounting. There's no magic happening behind the scenes, you'll still have to write the resolvers yourself. Read the Blips documentation about writing resolvers for subscriptions

const allTodosSubscription = `
  subscription allTodosSubscription {
    allTodos {
      id
      label
      completed
    }
  }
`

export default graphql(allTodosSubscription)(TodoList)

The withOperations HOC

The withOperations higher-order component creates components that have the ability to execute queries and mutations manually. The signature of withOperations is the same as that of graphql. It will not provide a data prop since it won't execute operations for you, but it will provide the queries and mutations props. It's useful for creating components that don't need to be connected to the store but may still execute mutations or read from it on demand, like when you have a component a few levels deep from a container and you don't want to pass callbacks all the way down.

The withClient HOC

Components created by the withClient higher-order component have access to the entire client instance. withClient accepts a single argument, the base component and it will provide a client prop. Messing with the client instance directly is risky, so this is not a recommended approach, but it's available if you consider that you need it. Event then, withOperations may still be enough.

Tips

See Blips documentation to read about tips