@tanstack/react-router 0.0.1-beta.194 → 0.0.1-beta.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -238,13 +238,13 @@
238
238
  push: (path, state) => {
239
239
  assignKey(state);
240
240
  queueTask(() => {
241
- opts.pushState(path, state);
241
+ opts.pushState(path, state, onUpdate);
242
242
  });
243
243
  },
244
244
  replace: (path, state) => {
245
245
  assignKey(state);
246
246
  queueTask(() => {
247
- opts.replaceState(path, state);
247
+ opts.replaceState(path, state, onUpdate);
248
248
  });
249
249
  },
250
250
  go: index => {
@@ -276,7 +276,8 @@
276
276
  stopBlocking();
277
277
  }
278
278
  };
279
- }
279
+ },
280
+ flush: () => opts.flush?.()
280
281
  };
281
282
  }
282
283
  function assignKey(state) {
@@ -289,25 +290,100 @@
289
290
  // }
290
291
  }
291
292
 
293
+ /**
294
+ * Creates a history object that can be used to interact with the browser's
295
+ * navigation. This is a lightweight API wrapping the browser's native methods.
296
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
297
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
298
+ * excessive calls to the history API. In some browsers, calling history.pushState or
299
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
300
+ * calls. This API smooths out those differences and ensures that your application
301
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
302
+ * but if you need to ensure that the browser state is up to date, you can use the
303
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
304
+ * @param opts
305
+ * @param opts.getHref A function that returns the current href (path + search + hash)
306
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
307
+ * @returns A history instance
308
+ */
292
309
  function createBrowserHistory(opts) {
293
310
  const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.search}${window.location.hash}`);
294
311
  const createHref = opts?.createHref ?? (path => path);
295
- const getLocation = () => parseLocation(getHref(), window.history.state);
312
+ let currentLocation = parseLocation(getHref(), window.history.state);
313
+ const getLocation = () => currentLocation;
314
+ let next;
315
+
316
+ // Because we are proactively updating the location
317
+ // in memory before actually updating the browser history,
318
+ // we need to track when we are doing this so we don't
319
+ // notify subscribers twice on the last update.
320
+ let tracking = true;
321
+
322
+ // We need to track the current scheduled update to prevent
323
+ // multiple updates from being scheduled at the same time.
324
+ let scheduled;
325
+
326
+ // This function is a wrapper to prevent any of the callback's
327
+ // side effects from causing a subscriber notification
328
+ const untrack = fn => {
329
+ tracking = false;
330
+ fn();
331
+ tracking = true;
332
+ };
333
+
334
+ // This function flushes the next update to the browser history
335
+ const flush = () => {
336
+ // Do not notify subscribers about this push/replace call
337
+ untrack(() => {
338
+ if (!next) return;
339
+ window.history[next.isPush ? 'pushState' : 'replaceState'](next.state, '', next.href);
340
+ // Reset the nextIsPush flag and clear the scheduled update
341
+ next = undefined;
342
+ scheduled = undefined;
343
+ });
344
+ };
345
+
346
+ // This function queues up a call to update the browser history
347
+ const queueHistoryAction = (type, path, state, onUpdate) => {
348
+ const href = createHref(path);
349
+
350
+ // Update the location in memory
351
+ currentLocation = parseLocation(href, state);
352
+
353
+ // Keep track of the next location we need to flush to the URL
354
+ next = {
355
+ href,
356
+ state,
357
+ isPush: next?.isPush || type === 'push'
358
+ };
359
+ // Notify subscribers
360
+ onUpdate();
361
+ if (!scheduled) {
362
+ // Schedule an update to the browser history
363
+ scheduled = Promise.resolve().then(() => flush());
364
+ }
365
+ };
296
366
  return createHistory({
297
367
  getLocation,
298
368
  subscriber: onUpdate => {
299
- window.addEventListener(pushStateEvent, onUpdate);
300
- window.addEventListener(popStateEvent, onUpdate);
369
+ window.addEventListener(pushStateEvent, () => {
370
+ currentLocation = parseLocation(getHref(), window.history.state);
371
+ onUpdate();
372
+ });
373
+ window.addEventListener(popStateEvent, () => {
374
+ currentLocation = parseLocation(getHref(), window.history.state);
375
+ onUpdate();
376
+ });
301
377
  var pushState = window.history.pushState;
302
378
  window.history.pushState = function () {
303
379
  let res = pushState.apply(history, arguments);
304
- onUpdate();
380
+ if (tracking) onUpdate();
305
381
  return res;
306
382
  };
307
383
  var replaceState = window.history.replaceState;
308
384
  window.history.replaceState = function () {
309
385
  let res = replaceState.apply(history, arguments);
310
- onUpdate();
386
+ if (tracking) onUpdate();
311
387
  return res;
312
388
  };
313
389
  return () => {
@@ -317,16 +393,13 @@
317
393
  window.removeEventListener(popStateEvent, onUpdate);
318
394
  };
319
395
  },
320
- pushState: (path, state) => {
321
- window.history.pushState(state, '', createHref(path));
322
- },
323
- replaceState: (path, state) => {
324
- window.history.replaceState(state, '', createHref(path));
325
- },
396
+ pushState: (path, state, onUpdate) => queueHistoryAction('push', path, state, onUpdate),
397
+ replaceState: (path, state, onUpdate) => queueHistoryAction('replace', path, state, onUpdate),
326
398
  back: () => window.history.back(),
327
399
  forward: () => window.history.forward(),
328
400
  go: n => window.history.go(n),
329
- createHref: path => createHref(path)
401
+ createHref: path => createHref(path),
402
+ flush
330
403
  });
331
404
  }
332
405
  function createHashHistory() {
@@ -383,6 +456,8 @@
383
456
  return (Math.random() + 1).toString(36).substring(7);
384
457
  }
385
458
 
459
+ // export type Expand<T> = T
460
+
386
461
  // type Compute<T> = { [K in keyof T]: T[K] } | never
387
462
 
388
463
  // type AllKeys<T> = T extends any ? keyof T : never
@@ -2287,6 +2362,7 @@
2287
2362
  return _extends.apply(this, arguments);
2288
2363
  }
2289
2364
 
2365
+ const useLayoutEffect$1 = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
2290
2366
  Route.__onInit = route => {
2291
2367
  Object.assign(route, {
2292
2368
  useMatch: (opts = {}) => {
@@ -2452,7 +2528,7 @@
2452
2528
  });
2453
2529
  function Navigate(props) {
2454
2530
  const router = useRouter();
2455
- React__namespace.useLayoutEffect(() => {
2531
+ useLayoutEffect$1(() => {
2456
2532
  router.navigate(props);
2457
2533
  }, []);
2458
2534
  return null;