7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RemixのダイナミックルーティングでOutletを更新しても、フォームの入力欄がリセットされない?

Last updated at Posted at 2022-07-04

RemixでOutletを使ったらinputの中身が更新されない?

ハマったこと

Remix(React Router v6)のOutletとダイナミックルーティングを組み合わせたとき、ページをDataLoaderでデータを取得しても、inputtextariaの初期値が更新されない。

解決策

inputtextariaにページ切り替え前後で異なるkeyを割り当てる。

具体的な状況

ディレクトリ(routes配下のみ抽出)

ディレクトリ構成図
app/routes
├── index.tsx
├── posts
│  ├── $slug.tsx
│  ├── admin
│  │  ├── $slug.tsx
│  │  ├── index.tsx
│  │  └── new.tsx
│  ├── admin.tsx
│  └── index.tsx
└── test.tsx

以下は今回話をする上で、重要なコンポーネントの概要です(相当簡略化しています)。

簡略化したadmin.tsxの構造
// DataLoaderの返り値
{
    posts: [
        {
            post: '記事1',
            title: '記事のタイトル1',
            markdown: '内容'
        }
    ]
}


<>
    { /** 記事のリンクのリスト **/ }
    <ul>
        {posts.map((post) => (
            <li key={post.slug}>
                <Link to={post.slug}>
                    {post.title}
                </Link>
            </li>
        ))}
    </ul>
    <main>
        // 配下の$slug.tsxを表示するOutlet
        <Outlet />
    </main>
</>
簡略化した$slug.tsxの構造
// DataLoaderの返り値
{
    post: {
        post: '記事1',
        title: '記事のタイトル1',
        markdown: '内容'
    }
}

<form method="put">
    <input
    type="text"
    name="title"
    defaultValue={post.title}
    />
    <input
    type="text"
    name="slug"
    label="Post Slug"
    defaultValue={post.slug}
    />
    <textarea
    name="markdown"
    label="Markdown"
    defaultValue={post.markdown}
    />
    <button type="submit">Create Post</button>
</form>

以下の画像はhttp://localhost:3000/posts/admin/90s-mixtapeにアクセスした際の表示です。
左は記事のリストであり、admin.tsxで描写されます。
右は記事の編集フォームで、記事のリストから$slug.tsxに渡されたparamsにも基づいて記事を検索し、現在の記事情報を表示します。

記事を選択
スクリーンショット 2022-07-04 17.57.01.png

別の記事を選択
スクリーンショット 2022-07-04 18.00.36.png

今回の問題は、一度記事を選択してから、別の記事に切り替えた際に発生しました。上記の画像からURLや記事のタイトルなどは変化しても、textariaの中身は変化がないことがわかります。console.logでDataloaderの出力が変化していることは確認しましたが、一部の要素が変化しません。useStateを使って各inputtextariaの状態を管理しても状況は変わらず…編集する記事を切り替えているのだから、全ての入力欄が変化してほしいところです。なお、textaria以外の入力欄も変更を加えると、それ以降変化しなくなりました。

勘違いしていたこと

ネステッドルーティングでは、URLが変更されると新しいOutlet要素がSSRで描画され、変更前のOutlet要素は一切捨てられるものだと考えていました。しかし、実際はSuspendなどを利用し、Reactのルールに則っているため、基本的には前のOutletの状態を引き継ぐようで、今回inputtextariaが更新されない理由はここにありました。

状態をリセットして再描画したい要素にはkeyを明示的につける

以前の状態を切り捨て1から描画させたいときは、リストでなくても明示的にkeyを指定し、その変化でReactに全く別の要素になったことを示しましょう。以下のようにすることで、ページを切り替えDataLoaderの値が変化したときにkeyも変化するため、1からinputtextariaを描写させることができます。

修正した$slug.tsxの概要
// 記事のユニーク値+入力欄のname
<form method="put">
    <input
    // タイトル用のキー
    key={post.slug + "title"}
    type="text"
    name="title"
    defaultValue={post.title}
    />
    <input
    // slug用のキー
    key={post.slug + "slug"}
    type="text"
    name="slug"
    label="Post Slug"
    defaultValue={post.slug}
    />
    <textarea
    // Markdown用のキー
    key={post.slug + "markdown"}
    name="markdown"
    label="Markdown"
    defaultValue={post.markdown}
    />
    <button type="submit">Create Post</button>
</form>

スクリーンショット 2022-07-04 18.39.58.png

スクリーンショット 2022-07-04 18.40.16.png

全ての入力欄が変化していることが読み取れます。

最後に

思わぬところでハマりだいぶ時間を使ってしまいました。RemixやNext.jsなどの便利なライブラリ、React18の新機能など色々新しいものを学ぶことも大事ですが、基礎的なところを疎かにしてはいけないですね…

参考資料

React.jsの地味だけど重要なkeyについて
React.js and Dynamic Children - Why the Keys are Important

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?