New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

graphql-hooks

Package Overview
Dependencies
Maintainers
1
Versions
89
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-hooks - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

.all-contributorsrc

11

CONTRIBUTING.md

@@ -38,4 +38,13 @@ # Welcome to GraphQL Hooks!

Release process:
## Updating Contributors list in README.md
You can add yourself or another contributor by either:
- Comment on an issue or pull request `@all-contributors please add <username> for <contributions>`
- `npm run contributors:add`
For more information on `@all-contributors` see it's [usage docs](https://allcontributors.org/docs/en/bot/usage)
### Release process:
- `npm test`

@@ -42,0 +51,0 @@ - `npm version <major|minor|patch>`

7

package.json
{
"name": "graphql-hooks",
"version": "3.0.0",
"version": "3.1.0",
"description": "Graphql Hooks",

@@ -10,3 +10,5 @@ "main": "src/index.js",

"test:coverage": "jest --coverage",
"prettier": "pretty-quick"
"prettier": "pretty-quick",
"contributors:add": "all-contributors add",
"contributors:generate": "all-contributors generate"
},

@@ -31,2 +33,3 @@ "keywords": [

"@babel/preset-react": "7.0.0",
"all-contributors-cli": "6.1.2",
"babel-jest": "24.1.0",

@@ -33,0 +36,0 @@ "husky": "1.3.1",

@@ -7,2 +7,3 @@ # graphql-hooks

[![npm version](https://badge.fury.io/js/graphql-hooks.svg)](https://badge.fury.io/js/graphql-hooks)
[![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors)

@@ -90,2 +91,3 @@ 🎣 Minimal hooks-first GraphQL client.

- [SSR](#SSR)
- [Pagination](#Pagination)
- [Authentication](#Authentication)

@@ -192,2 +194,5 @@ - [Fragments](#Fragments)

- `fetchOptionsOverrides`: Object - Specific overrides for this query. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) for info on what options can be passed
- `updateData(previousData, data)`: Function - Custom handler for merging previous & new query results; return value will replace `data` in `useQuery` return value
- `previousData`: Previous GraphQL query or `updateData` result
- `data`: New GraphQL query result

@@ -203,3 +208,4 @@ ### `useQuery` return value

- `data`: Object - the result of your GraphQL query
- `refetch`: Function - useful when refetching the same query after a mutation; NOTE this presets `skipCache=true`
- `refetch(options)`: Function - useful when refetching the same query after a mutation; NOTE this presets `skipCache=true` & will bypass the `options.updateData` function that was passed into `useQuery`. You can pass a new `updateData` into `refetch` if necessary.
- `options`: Object - options that will be merged into the `options` that were passed into `useQuery` (see above).
- `cacheHit`: Boolean - `true` if the query result came from the cache, useful for debugging

@@ -316,2 +322,126 @@ - `fetchError`: Object - Set if an error occured during the `fetch` call

### Pagination
[GraphQL Pagination](https://graphql.org/learn/pagination/) can be implemented in various ways and it's down to the consumer to decide how to deal with the resulting data from paginated queries. Take the following query as an example of offset pagination:
```javascript
export const allPostsQuery = `
query allPosts($first: Int!, $skip: Int!) {
allPosts(orderBy: createdAt_DESC, first: $first, skip: $skip) {
id
title
votes
url
createdAt
}
_allPostsMeta {
count
}
}
`;
```
In this query, the `$first` variable is used to limit the number of posts that are returned and the `$skip` variable is used to determine the offset at which to start. We can use these variables to break up large payloads into smaller chunks, or "pages". We could then choose to display these chunks as distinct pages to the user, or use an infinite loading approach and append each new chunk to the existing list of posts.
#### Separate pages
Here is an example where we display the paginated queries on separate pages:
```jsx
import { React, useState } from 'react';
import { useQuery } from 'graphql-hooks';
export default function PostList() {
// set a default offset of 0 to load the first page
const [skipCount, setSkipCount] = useState(0);
const { loading, error, data } = useQuery(allPostsQuery, {
variables: { skip: skipCount, first: 10 }
});
if (error) return <div>There was an error!</div>;
if (loading && !data) return <div>Loading</div>;
const { allPosts, _allPostsMeta } = data;
const areMorePosts = allPosts.length < _allPostsMeta.count;
return (
<section>
<ul>
{allPosts.map(post => (
<li key={post.id}>
<a href={post.url}>{post.title}</a>
</li>
))}
</ul>
<button
// reduce the offset by 10 to fetch the previous page
onClick={() => setSkipCount(skipCount - 10)}
disabled={skipCount === 0}
>
Previous page
</button>
<button
// increase the offset by 10 to fetch the next page
onClick={() => setSkipCount(skipCount + 10)}
disabled={!areMorePosts}
>
Next page
</button>
</section>
);
}
```
#### Infinite loading
Here is an example where we append each paginated query to the bottom of the current list:
```jsx
import { React, useState } from 'react';
import { useQuery } from 'graphql-hooks';
// use options.updateData to append the new page of posts to our current list of posts
const updateData = (prevData, data) => ({
...data,
allPosts: [...prevData.allPosts, ...data.allPosts]
});
export default function PostList() {
const [skipCount, setSkipCount] = useState(0);
const { loading, error, data } = useQuery(
allPostsQuery,
{ variables: { skip: skipCount, first: 10 } },
updateData
);
if (error) return <div>There was an error!</div>;
if (loading && !data) return <div>Loading</div>;
const { allPosts, _allPostsMeta } = data;
const areMorePosts = allPosts.length < _allPostsMeta.count;
return (
<section>
<ul>
{allPosts.map(post => (
<li key={post.id}>
<a href={post.url}>{post.title}</a>
</li>
))}
</ul>
{areMorePosts && (
<button
// set the offset to the current number of posts to fetch the next page
onClick={() => setSkipCount(allPosts.length)}
>
Show more
</button>
)}
</section>
);
}
```
### Authentication

@@ -328,1 +458,12 @@

Coming soon!
## Contributors
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<table><tr><td align="center"><a href="https://twitter.com/bmullan91"><img src="https://avatars1.githubusercontent.com/u/1939483?v=4" width="100px;" alt="Brian Mullan"/><br /><sub><b>Brian Mullan</b></sub></a><br /><a href="#question-bmullan91" title="Answering Questions">💬</a> <a href="https://github.com/nearfrorm/graphql-hooks/issues?q=author%3Abmullan91" title="Bug reports">🐛</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=bmullan91" title="Code">💻</a> <a href="#content-bmullan91" title="Content">🖋</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=bmullan91" title="Documentation">📖</a> <a href="#example-bmullan91" title="Examples">💡</a> <a href="#ideas-bmullan91" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-bmullan91" title="Maintenance">🚧</a> <a href="#review-bmullan91" title="Reviewed Pull Requests">👀</a></td><td align="center"><a href="https://jackdc.com"><img src="https://avatars0.githubusercontent.com/u/1485654?v=4" width="100px;" alt="Jack Clark"/><br /><sub><b>Jack Clark</b></sub></a><br /><a href="#question-jackdclark" title="Answering Questions">💬</a> <a href="https://github.com/nearfrorm/graphql-hooks/issues?q=author%3Ajackdclark" title="Bug reports">🐛</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=jackdclark" title="Code">💻</a> <a href="#content-jackdclark" title="Content">🖋</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=jackdclark" title="Documentation">📖</a> <a href="#example-jackdclark" title="Examples">💡</a> <a href="#ideas-jackdclark" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-jackdclark" title="Maintenance">🚧</a> <a href="#review-jackdclark" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=jackdclark" title="Tests">⚠️</a></td><td align="center"><a href="http://twitter.com/joezo"><img src="https://avatars1.githubusercontent.com/u/2870255?v=4" width="100px;" alt="Joe Warren"/><br /><sub><b>Joe Warren</b></sub></a><br /><a href="#question-Joezo" title="Answering Questions">💬</a> <a href="https://github.com/nearfrorm/graphql-hooks/issues?q=author%3AJoezo" title="Bug reports">🐛</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=Joezo" title="Code">💻</a> <a href="#content-Joezo" title="Content">🖋</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=Joezo" title="Documentation">📖</a> <a href="#example-Joezo" title="Examples">💡</a> <a href="#ideas-Joezo" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-Joezo" title="Maintenance">🚧</a> <a href="#review-Joezo" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=Joezo" title="Tests">⚠️</a></td><td align="center"><a href="http://simoneb.github.io"><img src="https://avatars1.githubusercontent.com/u/20181?v=4" width="100px;" alt="Simone Busoli"/><br /><sub><b>Simone Busoli</b></sub></a><br /><a href="#question-simoneb" title="Answering Questions">💬</a> <a href="https://github.com/nearfrorm/graphql-hooks/issues?q=author%3Asimoneb" title="Bug reports">🐛</a> <a href="https://github.com/nearfrorm/graphql-hooks/commits?author=simoneb" title="Documentation">📖</a></td></tr></table>
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

@@ -91,4 +91,11 @@ const React = require('react');

dispatch({ type: actionTypes.LOADING });
const result = await client.request(revisedOperation, revisedOpts);
let result = await client.request(revisedOperation, revisedOpts);
if (state.data && result.data && revisedOpts.updateData) {
if (typeof revisedOpts.updateData !== 'function') {
throw new Error('options.updateData must be a function');
}
result.data = revisedOpts.updateData(state.data, result.data);
}
if (revisedOpts.useCache && client.cache) {

@@ -95,0 +102,0 @@ client.cache.set(revisedCacheKey, result);

@@ -11,8 +11,6 @@ const React = require('react');

module.exports = function useQuery(query, opts = {}) {
const allOpts = { ...defaultOpts, ...opts };
const client = React.useContext(ClientContext);
const [calledDuringSSR, setCalledDuringSSR] = React.useState(false);
const [queryReq, state] = useClientRequest(query, {
...defaultOpts,
...opts
});
const [queryReq, state] = useClientRequest(query, allOpts);

@@ -34,4 +32,12 @@ if (client.ssrMode && opts.ssr !== false && !calledDuringSSR) {

...state,
refetch: () => queryReq({ skipCache: true })
refetch: (options = {}) =>
queryReq({
skipCache: true,
// don't call the updateData that has been passed into useQuery here
// reset to the default behaviour of returning the raw query result
// this can be overridden in refetch options
updateData: (_, data) => data,
...options
})
};
};

@@ -27,3 +27,3 @@ import React from 'react';

},
request: jest.fn().mockResolvedValue({ some: 'data' })
request: jest.fn().mockResolvedValue({ data: 'data' })
};

@@ -131,3 +131,3 @@ });

);
expect(state).toEqual({ cacheHit: false, loading: false, some: 'data' });
expect(state).toEqual({ cacheHit: false, loading: false, data: 'data' });
});

@@ -186,3 +186,3 @@

loading: false,
some: 'data'
data: 'data'
});

@@ -204,3 +204,3 @@ });

loading: false,
some: 'data'
data: 'data'
});

@@ -219,6 +219,90 @@ });

expect(mockClient.cache.set).toHaveBeenCalledWith('cacheKey', {
some: 'data'
data: 'data'
});
});
describe('options.updateRequest', () => {
it('is called with old & new data if the data has changed & the result is returned', async () => {
let fetchData, state;
const updateDataMock = jest.fn().mockReturnValue('merged data');
testHook(
() =>
([fetchData, state] = useClientRequest(TEST_QUERY, {
variables: { limit: 10 },
updateData: updateDataMock
})),
{ wrapper: Wrapper }
);
// first fetch to populate state
await fetchData();
mockClient.request.mockResolvedValueOnce({ data: 'new data' });
await fetchData({ variables: { limit: 20 } });
expect(updateDataMock).toHaveBeenCalledWith('data', 'new data');
expect(state).toEqual({
cacheHit: false,
data: 'merged data',
loading: false
});
});
it('is not called if there is no old data', async () => {
let fetchData;
const updateDataMock = jest.fn();
testHook(
() =>
([fetchData] = useClientRequest(TEST_QUERY, {
variables: { limit: 10 },
updateData: updateDataMock
})),
{ wrapper: Wrapper }
);
await fetchData();
expect(updateDataMock).not.toHaveBeenCalled();
});
it('is not called if there is no new data', async () => {
let fetchData;
const updateDataMock = jest.fn();
testHook(
() =>
([fetchData] = useClientRequest(TEST_QUERY, {
variables: { limit: 10 },
updateData: updateDataMock
})),
{ wrapper: Wrapper }
);
await fetchData();
mockClient.request.mockReturnValueOnce({ errors: ['on no!'] });
await fetchData({ variables: { limit: 20 } });
expect(updateDataMock).not.toHaveBeenCalled();
});
it('throws if updateData is not a function', async () => {
let fetchData;
testHook(
() =>
([fetchData] = useClientRequest(TEST_QUERY, {
variables: { limit: 10 },
updateData: 'do I look like a function to you?'
})),
{ wrapper: Wrapper }
);
// first fetch to populate state
await fetchData();
expect(fetchData({ variables: { limit: 20 } })).rejects.toThrow(
'options.updateData must be a function'
);
});
});
});
});

@@ -56,3 +56,3 @@ import React from 'react';

it('returns initial state from useClientRequest & refetch', () => {
it('returns initial state from useClientRequest, refetch & fetchMore', () => {
let state;

@@ -71,5 +71,51 @@ testHook(() => (state = useQuery(TEST_QUERY)), { wrapper: Wrapper });

refetch();
expect(mockQueryReq).toHaveBeenCalledWith({ skipCache: true });
expect(mockQueryReq).toHaveBeenCalledWith({
skipCache: true,
updateData: expect.any(Function)
});
});
it('merges options when refetch is called', () => {
let refetch;
testHook(
() =>
({ refetch } = useQuery(TEST_QUERY, {
variables: { skip: 0, first: 10 }
})),
{
wrapper: Wrapper
}
);
const updateData = () => {};
refetch({
extra: 'option',
variables: { skip: 10, first: 10, extra: 'variable' },
updateData
});
expect(mockQueryReq).toHaveBeenCalledWith({
skipCache: true,
extra: 'option',
variables: { skip: 10, first: 10, extra: 'variable' },
updateData
});
});
it('gets updateData to replace the result by default', () => {
let refetch;
testHook(
() =>
({ refetch } = useQuery(TEST_QUERY, {
variables: { skip: 0, first: 10 }
})),
{
wrapper: Wrapper
}
);
mockQueryReq.mockImplementationOnce(({ updateData }) => {
return updateData('previousData', 'data');
});
refetch();
expect(mockQueryReq).toHaveReturnedWith('data');
});
it('sends the query on mount if no data & no error', () => {

@@ -76,0 +122,0 @@ testHook(() => useQuery(TEST_QUERY), { wrapper: Wrapper });

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc