question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is there a way to use jotai synced with NextJs router?

See original GitHub issue

At first let me thank you for your awsome store management, There is a point, storing some data (state) on the query field of the URL is necessary some times, But I don’t like to handle this directly, I think syncing store with the URL will be a good idea. In this situation we will have for example a NextRouterAtom which is connected to the URL and changing query or state will cause changing the other one. What is your idea about that?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:23 (8 by maintainers)

github_iconTop GitHub Comments

3reactions
erandowcommented, May 23, 2021

Sure I will work on that, Just let me to read the existing packages which are for connecting router to the Redux and other store managements to see how they work exactly, I will inform you soon.

1reaction
neckaroscommented, Dec 6, 2021

Well i put together a version that work for get and set. But i know Jotai since yesterday so i took some shortcut and my implementation might be very very bad 😦

I pass the router as an argument to avoid adding a dependency to utils

some shortcuts: I assume value is string but could be a string array. Need to check the type and act accordingly (in get and set) => strongly types to either String or String[] Not sure why my value was not initialized without route change so i added a callback() in the const subscribe That’s what delayInit is for! removed the option and set it to true has Router is delayed

import { PrimitiveAtom } from "jotai"
import { atomWithStorage } from "jotai/utils"
import type { UrlObject } from 'url';

type Unsubscribe = () => void
type AtomWithRouterQueryValue = string | string[];
type SyncStorage = {
    getItem: (key: string) => AtomWithRouterQueryValue
    setItem: (key: string, newValue: AtomWithRouterQueryValue) => void
    delayInit?: boolean
    subscribe?: (key: string, callback: (value: AtomWithRouterQueryValue) => void) => Unsubscribe
  }
type Url = UrlObject | string;
interface TransitionOptions {
  shallow?: boolean;
  locale?: string | false;
  scroll?: boolean;
}

interface NextJSRouter {
  events: {
    on(type: 'routeChangeComplete', handler: (...evts: any[]) => void): void
  },
  pathname: string,
  query: {[key: string]: string | string[]}
  push:(url: Url, as?: Url, options?: TransitionOptions) => void
}
export function atomWithRouterQuery(
    key: string,
    initialValue: AtomWithRouterQueryValue,
    router: NextJSRouter,
    options?: {
      shallow?: boolean, //see NextJS router documentation: https://nextjs.org/docs/routing/shallow-routing
      subscribe?: (callback: () => void) => () => void,
    }
  ): PrimitiveAtom<AtomWithRouterQueryValue> {
    const subscribe =
      options?.subscribe ||
      ((callback) => {
        router.events.on('routeChangeComplete', callback)
        return () => {
          window.removeEventListener('routeChangeComplete', callback)
        }
      })
    const routerStorage: SyncStorage = {
      getItem: (key) => {
        const storedValue = router.query[key];
        if (storedValue === null) {
          throw new Error('no value stored')
        }
        return storedValue
      },
      setItem: (key, newValue) => {
        router.push({pathname: router.pathname, query: {...router.query, [key]: newValue}}, undefined, {shallow: options?.shallow})
      },
      delayInit: true,
      subscribe: (key, setValue) => {
        const callback = () => {
            const storedValue = router.query[key];
          if (storedValue !== null) {
            setValue(storedValue)
          } else {
            setValue(initialValue)
          }
        }
        return subscribe(callback)
      },
    }
    return atomWithStorage(key, initialValue, routerStorage)
  }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Next.js — Jotai, primitive and flexible state management for ...
Jotai has support for hydration of atoms with useHydrateAtoms . The documentation for the hook can be seen here. Sync with router. It's...
Read more >
Up & Running with Jotai, Typescript and NextJS
Simple, elegant state management with Jotai and type-safety with Typescript. Today we will learn how to use them together.
Read more >
next-jotai-sync-location - npm package - Snyk
Sync jotai atoms with Next.​​ As dai-shi mentioned, there is no easy way to keep atoms in sync with window. location or the...
Read more >
Next.js and Jotai: Share state across pages | by Markus Tripp
You can use React's Context to pass data through the component tree. Or choose one of the many state managers available. In this...
Read more >
/docs/guides/nextjs.mdx | jotai@v1.7.2 | Deno
---title: Next.jsdescription: How to use Jotai with Next.jsnav: 3.04--- ## Hydration ... It's possible to sync Jotai with the router.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found