UNPKG

graphql-hooks

Version:
242 lines (194 loc) 7.08 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var GraphQLClient = require('./GraphQLClient-719063e6.js'); var React = _interopDefault(require('react')); var ClientContext = React.createContext(); ClientContext.displayName = 'ClientContext'; var actionTypes = { RESET_STATE: 'RESET_STATE', LOADING: 'LOADING', CACHE_HIT: 'CACHE_HIT', REQUEST_RESULT: 'REQUEST_RESULT' }; function reducer(state, action) { switch (action.type) { case actionTypes.RESET_STATE: return action.initialState; case actionTypes.LOADING: if (state.loading) { return state; // saves a render cycle as state is the same } return GraphQLClient._objectSpread({}, state, { loading: true }); case actionTypes.CACHE_HIT: if (state.cacheHit) { // we can be sure this is the same cacheKey hit // because we dispatch RESET_STATE if it changes return state; } return GraphQLClient._objectSpread({}, action.result, { cacheHit: true, loading: false }); case actionTypes.REQUEST_RESULT: return GraphQLClient._objectSpread({}, action.result, { cacheHit: false, loading: false }); default: return state; } } /* options include: opts.variables: Object opts.operationName: String opts.fetchOptionsOverrides: Object opts.skipCache: Boolean */ function useClientRequest(query, initialOpts) { if (initialOpts === void 0) { initialOpts = {}; } var client = React.useContext(ClientContext); var isMounted = React.useRef(true); var activeCacheKey = React.useRef(null); var operation = { query: query, variables: initialOpts.variables, operationName: initialOpts.operationName }; var cacheKey = client.getCacheKey(operation, initialOpts); var isDeferred = initialOpts.isMutation || initialOpts.isManual; var initialCacheHit = initialOpts.skipCache || !client.cache ? null : client.cache.get(cacheKey); var initialState = GraphQLClient._objectSpread({}, initialCacheHit, { cacheHit: !!initialCacheHit, loading: isDeferred ? false : !initialCacheHit }); var _React$useReducer = React.useReducer(reducer, initialState), state = _React$useReducer[0], dispatch = _React$useReducer[1]; // NOTE: state from useReducer is only initialState on the first render // in subsequent renders the operation could have changed // if so the state would be invalid, this effect ensures we reset it back var stringifiedCacheKey = JSON.stringify(cacheKey); React.useEffect(function () { if (!initialOpts.updateData) { // if using updateData we can assume that the consumer cares about the previous data dispatch({ type: actionTypes.RESET_STATE, initialState: initialState }); } }, [stringifiedCacheKey]); // eslint-disable-line react-hooks/exhaustive-deps React.useEffect(function () { isMounted.current = true; return function () { isMounted.current = false; }; }, []); // arguments to fetchData override the useClientRequest arguments function fetchData(newOpts) { if (!isMounted.current) return Promise.resolve(); var revisedOpts = GraphQLClient._objectSpread({}, initialOpts, newOpts); var revisedOperation = GraphQLClient._objectSpread({}, operation, { variables: revisedOpts.variables, operationName: revisedOpts.operationName }); var revisedCacheKey = client.getCacheKey(revisedOperation, revisedOpts); // NOTE: There is a possibility of a race condition whereby // the second query could finish before the first one, dispatching an old result // see https://github.com/nearform/graphql-hooks/issues/150 activeCacheKey.current = revisedCacheKey; var cacheHit = revisedOpts.skipCache || !client.cache ? null : client.cache.get(revisedCacheKey); if (cacheHit) { dispatch({ type: actionTypes.CACHE_HIT, result: cacheHit }); return Promise.resolve(cacheHit); } dispatch({ type: actionTypes.LOADING }); return client.request(revisedOperation, revisedOpts).then(function (result) { 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) { client.cache.set(revisedCacheKey, result); } if (isMounted.current && revisedCacheKey === activeCacheKey.current) { dispatch({ type: actionTypes.REQUEST_RESULT, result: result }); } return result; }); } return [fetchData, state]; } var defaultOpts = { useCache: true }; function useQuery(query, opts) { if (opts === void 0) { opts = {}; } var allOpts = GraphQLClient._objectSpread({}, defaultOpts, opts); var client = React.useContext(ClientContext); var _React$useState = React.useState(false), calledDuringSSR = _React$useState[0], setCalledDuringSSR = _React$useState[1]; var _useClientRequest = useClientRequest(query, allOpts), queryReq = _useClientRequest[0], state = _useClientRequest[1]; if (client.ssrMode && opts.ssr !== false && !calledDuringSSR) { // result may already be in the cache from previous SSR iterations if (!state.data && !state.error) { var p = queryReq(); client.ssrPromises.push(p); } setCalledDuringSSR(true); } var stringifiedAllOpts = JSON.stringify(allOpts); React.useEffect(function () { queryReq(); }, [query, stringifiedAllOpts]); // eslint-disable-line react-hooks/exhaustive-deps return GraphQLClient._objectSpread({}, state, { refetch: function refetch(options) { if (options === void 0) { options = {}; } return queryReq(GraphQLClient._objectSpread({ 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: function updateData(_, data) { return data; } }, options)); } }); } var useManualQuery = function useManualQuery(query, options) { return useClientRequest(query, GraphQLClient._objectSpread({ useCache: true, isManual: true }, options)); }; var useMutation = function useMutation(query, options) { return useClientRequest(query, GraphQLClient._objectSpread({ isMutation: true }, options)); }; exports.GraphQLClient = GraphQLClient.GraphQLClient; exports.ClientContext = ClientContext; exports.useClientRequest = useClientRequest; exports.useManualQuery = useManualQuery; exports.useMutation = useMutation; exports.useQuery = useQuery;