UNPKG

react-instantsearch-core

Version:
218 lines (212 loc) 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useInstantSearchApi = useInstantSearchApi; var _InstantSearch = _interopRequireWildcard(require("instantsearch.js/cjs/lib/InstantSearch")); var _react = require("react"); var _shim = require("use-sync-external-store/shim"); var _version = _interopRequireDefault(require("../version")); var _dequal = require("./dequal"); var _useForceUpdate = require("./useForceUpdate"); var _useInstantSearchServerContext = require("./useInstantSearchServerContext"); var _useInstantSearchSSRContext = require("./useInstantSearchSSRContext"); var _useRSCContext = require("./useRSCContext"); var _warn = require("./warn"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var defaultUserAgents = ["react (".concat(_react.version, ")"), "react-instantsearch (".concat(_version.default, ")"), "react-instantsearch-core (".concat(_version.default, ")")]; var serverUserAgent = "react-instantsearch-server (".concat(_version.default, ")"); var nextUserAgent = function nextUserAgent(nextVersion) { return nextVersion ? "next.js (".concat(nextVersion, ")") : null; }; function useInstantSearchApi(props) { var forceUpdate = (0, _useForceUpdate.useForceUpdate)(); var serverContext = (0, _useInstantSearchServerContext.useInstantSearchServerContext)(); var serverState = (0, _useInstantSearchSSRContext.useInstantSearchSSRContext)(); var waitingForResultsRef = (0, _useRSCContext.useRSCContext)(); var initialResults = serverState === null || serverState === void 0 ? void 0 : serverState.initialResults; var prevPropsRef = (0, _react.useRef)(props); var shouldRenderAtOnce = serverContext || initialResults || waitingForResultsRef; var searchRef = (0, _react.useRef)(null); // As we need to render on mount with SSR, using the local ref above in `StrictMode` will // create and start two instances of InstantSearch. To avoid this, we instead discard it and use // an upward ref from `InstantSearchSSRContext` as it has already been mounted a second time at this point. if (serverState) { searchRef = serverState.ssrSearchRef; } if (searchRef.current === null) { // We don't use the `instantsearch()` function because it comes with other // top-level APIs that we don't need. // See https://github.com/algolia/instantsearch/blob/5b529f43d8acc680f85837eaaa41f7fd03a3f833/src/index.es.ts#L63-L86 var search = new _InstantSearch.default(props); search._schedule = function _schedule(cb) { search._schedule.queue.push(cb); clearTimeout(search._schedule.timer); search._schedule.timer = setTimeout(function () { search._schedule.queue.forEach(function (callback) { callback(); }); search._schedule.queue = []; }, 0); }; search._schedule.queue = []; if (shouldRenderAtOnce) { // InstantSearch.js has a private Initial Results API that lets us inject // results on the search instance. // On the server, we default the initial results to an empty object so that // InstantSearch.js doesn't schedule a search that isn't used, leading to // an additional network request. (This is equivalent to monkey-patching // `scheduleSearch` to a noop.) search._initialResults = initialResults || {}; // We don't rely on the `defer` to reset the schedule search, but will call // `search._resetScheduleSearch()` manually in the effect after children // mount in `InstantSearch`. search._manuallyResetScheduleSearch = true; } addAlgoliaAgents(props.searchClient, [].concat(defaultUserAgents, [serverContext && serverUserAgent, nextUserAgent(getNextVersion())])); // On the server, we start the search early to compute the search parameters. // On SSR, we start the search early to directly catch up with the lifecycle // and render. if (shouldRenderAtOnce) { search.start(); } if (serverContext) { // We notify `getServerState()` of the InstantSearch internals to retrieve // the server state and pass it to the render on SSR. serverContext.notifyServer({ search: search }); } warnNextRouter(props.routing); warnNextAppDir(Boolean(waitingForResultsRef)); searchRef.current = search; } { var _search = searchRef.current; var prevProps = prevPropsRef.current; if (prevProps.indexName !== props.indexName) { _search.helper.setIndex(props.indexName || '').search(); prevPropsRef.current = props; } if (prevProps.searchClient !== props.searchClient) { process.env.NODE_ENV === 'development' ? (0, _warn.warn)(false, 'The `searchClient` prop of `<InstantSearch>` changed between renders, which may cause more search requests than necessary. If this is an unwanted behavior, please provide a stable reference: https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/#widget-param-searchclient') : void 0; addAlgoliaAgents(props.searchClient, [].concat(defaultUserAgents, [serverContext && serverUserAgent])); _search.mainHelper.setClient(props.searchClient).search(); prevPropsRef.current = props; } if (prevProps.onStateChange !== props.onStateChange) { _search.onStateChange = props.onStateChange; prevPropsRef.current = props; } if (prevProps.searchFunction !== props.searchFunction) { // Updating the `searchFunction` to `undefined` is not supported by // InstantSearch.js, so it will throw an error. // This is a fair behavior until we add an update API in InstantSearch.js. _search._searchFunction = props.searchFunction; prevPropsRef.current = props; } if (prevProps.stalledSearchDelay !== props.stalledSearchDelay) { var _props$stalledSearchD; // The default `stalledSearchDelay` in InstantSearch.js is 200ms. // We need to reset it when it's undefined to get back to the original value. _search._stalledSearchDelay = (_props$stalledSearchD = props.stalledSearchDelay) !== null && _props$stalledSearchD !== void 0 ? _props$stalledSearchD : 200; prevPropsRef.current = props; } if (!(0, _dequal.dequal)(prevProps.future, props.future)) { _search.future = _objectSpread(_objectSpread({}, _InstantSearch.INSTANTSEARCH_FUTURE_DEFAULTS), props.future); prevPropsRef.current = props; } // Updating the `routing` prop is not supported because InstantSearch.js // doesn't let us change it. This might not be a problem though, because `routing` // shouldn't need to be dynamic. // If we find scenarios where `routing` needs to change, we can always expose // it privately on the InstantSearch instance. Another way would be to // manually inject the routing middleware in this library, and not rely // on the provided `routing` prop. } var cleanupTimerRef = (0, _react.useRef)(null); var store = (0, _shim.useSyncExternalStore)((0, _react.useCallback)(function () { var search = searchRef.current; // Scenario 1: the component mounts. if (cleanupTimerRef.current === null) { // On SSR, the instance is already started so we don't start it again. if (!search.started) { search.start(); forceUpdate(); } } // Scenario 2: the component updates. else { // We cancel the previous cleanup function because we don't want to // dispose the search during an update. clearTimeout(cleanupTimerRef.current); search._preventWidgetCleanup = false; } return function () { function cleanup() { search.dispose(); } clearTimeout(search._schedule.timer); // We clean up only when the component that uses this subscription unmounts, // but not when it updates, because it would dispose the instance, which // would remove all the widgets and break routing. // Executing the cleanup function in a `setTimeout()` lets us cancel it // in the next effect. // (There might be better ways to do this.) cleanupTimerRef.current = setTimeout(cleanup); // We need to prevent the `useWidget` cleanup function so that widgets // are not removed before the instance is disposed, triggering // an unwanted search request. search._preventWidgetCleanup = true; }; }, [forceUpdate]), function () { return searchRef.current; }, function () { return searchRef.current; }); return store; } function addAlgoliaAgents(searchClient, userAgents) { if (typeof searchClient.addAlgoliaAgent !== 'function') { return; } userAgents.filter(Boolean).forEach(function (userAgent) { searchClient.addAlgoliaAgent(userAgent); }); } function warnNextRouter(routing) { if (process.env.NODE_ENV === 'development') { var _routing$router; if (!routing || typeof window === 'undefined' || !('__NEXT_DATA__' in window)) { return; } var isUsingNextRouter = // @ts-expect-error: _isNextRouter is only set on the Next.js router routing !== true && (routing === null || routing === void 0 ? void 0 : (_routing$router = routing.router) === null || _routing$router === void 0 ? void 0 : _routing$router._isNextRouter); process.env.NODE_ENV === 'development' ? (0, _warn.warn)(isUsingNextRouter, "\nYou are using Next.js with InstantSearch without the \"react-instantsearch-router-nextjs\" package.\nThis package is recommended to make the routing work correctly with Next.js.\nPlease check its usage instructions: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch-router-nextjs\n\nYou can ignore this warning if you are using a custom router that suits your needs, it won't be outputted in production builds.") : void 0; } } function warnNextAppDir(isRscContextDefined) { var _next; if (!(process.env.NODE_ENV === 'development') || typeof window === 'undefined' || isRscContextDefined) { return; } process.env.NODE_ENV === 'development' ? (0, _warn.warn)(Boolean((_next = window.next) === null || _next === void 0 ? void 0 : _next.appDir) === false, "\nWe've detected you are using Next.js with the App Router.\nWe released an **experimental** package called \"react-instantsearch-nextjs\" that makes SSR work with the App Router.\nPlease check its usage instructions: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#with-nextjs\n\nThis warning will not be outputted in production builds.") : void 0; } /** * Gets the version of Next.js if it is available in the `window` object, * otherwise it returns the NEXT_RUNTIME environment variable (in SSR), * which is either `nodejs` or `edge`. */ function getNextVersion() { var _next2, _process$env; return typeof window !== 'undefined' && ((_next2 = window.next) === null || _next2 === void 0 ? void 0 : _next2.version) || (typeof process !== 'undefined' ? (_process$env = process.env) === null || _process$env === void 0 ? void 0 : _process$env.NEXT_RUNTIME : undefined); }