😸

nextjs上でPOSTしてページ遷移後にリロードするとSafariとChromeで挙動が違う

2022/03/30に公開

追記:2022/04/21

  • issueのほうがクローズされました。要はブラウザのバグなので、Next.jsでは特に修正するつもりはない、というスタンスのようです。Safariの対応を首を長くして待つか、この方法を利用しないことをおすすめします。

※この問題自体はすでにissueに起票しています。

https://github.com/vercel/next.js/issues/35716

何が起きるか

タイトル通りではありますが、再現動画は以下のとおりです。

Google Chrome

Google Chrome

Safari

Safari

何が起きているか

サンプルコードは以下に置いてますが、抜粋してコードを示すと次のようになります。

https://github.com/Himenon/nextjs-post-and-reload-problem

// pages/index.tsx
function Page() {
  return (
    <div>
      <form action="/register" method="POST">
        <button type="submit">Submit(POST)</button>
      </form>
    </div>
  );
}

export default Page;
// pages/register.tsx
import React from "react";
import { GetServerSideProps, GetServerSidePropsContext, NextPage } from "next/types";

export type Props = {
  requestMethod: string;
};

const Page: NextPage<Props> = (props) => {
  return (
    <div>
      <h1>Request Method: {props.requestMethod}</h1>
    </div>
  );
};

export const getServerSideProps: GetServerSideProps = async ({ req }: GetServerSidePropsContext) => {
  return {
    props: {
      requestMethod: req.method,
    },
  };
};

export default Page;

単純にフォームでPOSTしているだけですが、ChromeとSafariで挙動が違います。

なぜ発生するか

nextjs内部的にどうやらwindow.history.replaceStateを実行しているようで、これがブラウザの実装差異を踏み抜いているようです。

どっちが真なのかStackOverflowに投げてみています。

https://stackoverflow.com/questions/71672272/what-is-the-correct-specification-for-reloading-after-rewriting-using-window-his

WebKit Bugzillaにはすでに先人がいて、2019年時点から発生しているようです。

https://bugs.webkit.org/show_bug.cgi?id=202963

whatwgの仕様を見る限りだと明確にどちらか正しいとは書いていないようにも(抽象的に書いているようにも)見えます。

https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps

まとめ

じゃあどうするか、という話ですが、nextjs側(ライブラリ側)で対応されればブラウザ間の挙動差異はなくなると考えられます。ブラウザの修正の方はIssueが立ってから3年経過しても動きはないので期待はできないでしょう。

ということで、現状はnextjsにおいてPOSTで遷移するのはSafariで挙動が違うのでやめて置くのが無難だろうという話でした。

Discussion