Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query params not updated when read from persistent layout #2110

Closed
Joehoel opened this issue Nov 28, 2024 · 1 comment
Closed

Query params not updated when read from persistent layout #2110

Joehoel opened this issue Nov 28, 2024 · 1 comment

Comments

@Joehoel
Copy link

Joehoel commented Nov 28, 2024

Versions:

  • @inertiajs/react version: ^1.0.0

Describe the problem:

I'm facing an issue where the query state is not being updated after a navigation initiated from a persistent layout. Using nuqs to manage the params in a type safe manner.

2024-11-28.at.20.08.00.mp4

Expected behaviour is that the previous button links to ?activeId=1 which was set by the nextButton navigating to ?previousId=1

See step 3 for layout file demonstrated in video.

Steps to reproduce:

1. Create a NuqsAdapter for Inertia

import mitt from "mitt";
import { useEffect, useState } from "react";
import {
  type unstable_AdapterOptions as AdapterOptions,
  unstable_createAdapterProvider as createAdapterProvider,
  renderQueryString,
} from "nuqs/adapters/custom";
import { router } from "@inertiajs/react";

const emitter = mitt<{ update: URLSearchParams }>();

function updateUrl(search: URLSearchParams, options: AdapterOptions) {
  const url = new URL(location.href);
  url.search = renderQueryString(search);
  router.visit(url, {
    replace: options.history === "replace",
    preserveScroll: !options.scroll,
    preserveState: options.shallow,
  });
  emitter.emit("update", search);
}

function useNuqsInertiaAdapter() {
  const [searchParams, setSearchParams] = useState(() => {
    if (typeof location === "undefined") {
      return new URLSearchParams();
    }
    return new URLSearchParams(location.search);
  });
  useEffect(() => {
    // Popstate event is only fired when the user navigates
    // via the browser's back/forward buttons.
    const onPopState = () => {
      setSearchParams(new URLSearchParams(location.search));
    };
    emitter.on("update", setSearchParams);
    window.addEventListener("popstate", onPopState);
    return () => {
      emitter.off("update", setSearchParams);
      window.removeEventListener("popstate", onPopState);
    };
  }, []);
  return {
    searchParams,
    updateUrl,
  };
}

export const NuqsAdapter = createAdapterProvider(useNuqsInertiaAdapter);

2. Wrap app with adapter

import { PropsWithChildren } from "react";
import { NuqsAdapter } from "@/lib/nuqs-inertia-adapter";

export function GlobalLayout({ children }: PropsWithChildren) {
  return <NuqsAdapter>{children}</NuqsAdapter>;
}

3. Create persistent layout

import { useIdState } from "@/lib/state";
import { Link } from "@inertiajs/react";
import { PropsWithChildren } from "react";

export function Layout({ children }: PropsWithChildren) {
    const { activeId, previousId } = useIdState();

    return (
        <div>
            Active: {activeId}
            <br />
            Previous: {previousId}
            {children}
            <Link href={route("page")} data={{ activeId: previousId }}>
                Previous
            </Link>
            <Link href={route("page")} data={{ previousId: activeId }}>
                Next
            </Link>
        </div>
    );
}

4. Create state hook

import { parseAsInteger, useQueryState } from "nuqs";

export function useIdState() {
    const [activeId, setActiveId] = useQueryState(
        "activeId",
        parseAsInteger.withOptions({ shallow: true }).withDefault(1)
    );

    const [previousId, setPreviousId] = useQueryState(
        "previousId",
        parseAsInteger.withOptions({ shallow: true })
    );

    return {
        activeId,
        setActiveId,
        previousId,
        setPreviousId,
    };
}
@franky47
Copy link

It's likely an issue with the nuqs adapter, not Inertia. When clicking a Link, there is likely nothing that fires in the nuqs adapter to tell it to re-render with the updated searchParams prop. In the past we solved this by patching the history.{push,replace}State methods to listen for changes, but there may be a better way in each framework's internal API for getting a reactive value when the URL changes.

I think this issue can be closed and the discussion moved back to 47ng/nuqs#787.

@Joehoel Joehoel closed this as completed Nov 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants