【ITニュース解説】Managing URL parameters as state in React
2025年09月17日に「Dev.to」が公開したITニュース「Managing URL parameters as state in React」について初心者にもわかりやすく解説しています。
ITニュース概要
ReactアプリでURLパラメータを状態として管理する方法。`useState()`に似た`useRouteState()`フックを活用し、URLパラメータを直感的に扱える。これにより、状態管理がシンプルになり、型安全なルーティングも可能。
ITニュース解説
Webアプリケーションでは、URLの末尾に「?search=keyword」のような形で追加されるクエリパラメーターや、「/users/123」のようにパスの一部として組み込まれるパスパラメーターがよく使われる。これらのURLに含まれる情報は、Webページがどのような内容を表示すべきかを決定するための重要な要素であり、アプリケーションの状態(ステート)の一部と見なすことができる。例えば、検索結果ページで検索キーワードがURLに含まれていれば、そのURLを共有することで同じ検索結果を再現できる。
Reactのようなモダンなフレームワークでは、アプリケーションの表示内容を決定するデータや状態を「ステート」として管理するのが一般的だ。通常、コンポーネント内部のローカルなステートはuseStateというフックを使って管理する。しかし、URLパラメーターもまた、アプリケーションの公開された、共有可能な状態であるため、このURLパラメーターもuseStateと同じようなReact流の方法で管理できると、開発はより直感的でスムーズになる。この記事では、まさにそのようなアプローチを提案している。
このアプローチを実現するために、useStateに非常によく似たuseRouteStateというカスタムフックが導入されている。このフックを使うことで、URLのパラメーターをアプリケーションのステートとして扱い、読み書きが可能になる。
具体例として、画面上に表示される図形の位置情報coordsをuseStateで管理しているコンポーネントを考えてみよう。
1export const App = () => { 2 let [{coords}, setState] = useState({coords: {}}); // ローカルステートでcoordsを管理 3 let setPosition = () => { 4 setState((state) => ({ 5 ...state, 6 coords: { 7 x: Math.floor(100 * Math.random()), 8 y: Math.floor(100 * Math.random()), 9 }, 10 })); 11 }; 12 return ( 13 <main> 14 <Shape x={coords.x} y={coords.y}/> {/* coordsを使って図形を表示 */} 15 <p><button onClick={setPosition}>Move</button></p> 16 </main> 17 ); 18};
このコードでは、coordsというオブジェクトがコンポーネント内部の状態として管理され、setPosition関数が呼び出されるとcoordsが更新され、それに応じて図形Shapeの位置が変わる。
これをuseRouteStateを使ってURLパラメーターで管理するように変更すると、以下のようになる。
1export const App = () => { 2 let [{query}, setState] = useRouteState('/'); // URLクエリパラメーターを管理 3 let setPosition = () => { 4 setState((state) => ({ 5 ...state, 6 query: { // URLクエリパラメーターとしてxとyを更新 7 x: Math.floor(100 * Math.random()), 8 y: Math.floor(100 * Math.random()), 9 }, 10 })); 11 }; 12 return ( 13 <main> 14 <Shape x={query.x} y={query.y}/> {/* URLクエリパラメーターを使って図形を表示 */} 15 <p><button onClick={setPosition}>Move</button></p> 16 </main> 17 ); 18};
この変更では、useStateがcoordsというローカルなデータを管理していた部分が、useRouteStateが管理するqueryというURLクエリパラメーターのデータに置き換わっている。queryオブジェクトの中には、図形のX座標とY座標(xとy)が含まれると想定される。setPosition関数でsetStateを呼び出すと、queryオブジェクトが更新され、それがURLのクエリパラメーターとして反映される。これにより、ブラウザのアドレスバーに/?x=50&y=30のような形で位置情報が表示され、このURLを共有すれば常に同じ位置に図形が表示されるようになる。
useStateとuseRouteStateのAPIが非常に似ているため、ローカルなステート管理からURLパラメーターを使ったステート管理への移行が直感的でスムーズに行えるのが大きなメリットだ。
さらにこのアプローチは、「型安全なルーティング」という考え方と組み合わせることで、より強力になる。型安全とは、プログラムが扱うデータの「型」(それが数値なのか、文字列なのか、特定の形式なのかなど)を明確に定義し、型が間違っている場合にエラーを検出できるようにすることだ。これにより、開発中のバグを減らし、コードの信頼性を高めることができる。
型安全なルーティングを実現するために、記事ではcreateURLSchemaという関数とzodというバリデーションライブラリを使って、URLの構造とパラメーターの型を定義する方法を示している。
1import {createURLSchema} from 'url-shape'; 2import {z} from 'zod'; 3 4const {url} = createURLSchema({ 5 '/shapes/:id': { // URLのパスパターンを定義 6 params: z.object({ // パスパラメーターの型定義 7 id: z.coerce.number(), // idは数値に変換されることを保証 8 }), 9 query: z.optional( // クエリパラメーターはオプション 10 z.object({ // クエリパラメーターの型定義 11 x: z.coerce.number(), // xは数値に変換される 12 y: z.coerce.number(), // yは数値に変換される 13 }) 14 ), 15 }, 16});
このコードでは、/shapes/:idというURLパターンを定義している。:idの部分は動的な値で、idという名前のパスパラメーターとして扱われる。z.coerce.number()は、このidがどんな形式でURLに含まれていても、最終的に数値として扱われることを保証する。queryの部分では、xとyというクエリパラメーターがオプションとして定義されており、これらも数値として扱われる。このスキーマを定義することで、useRouteStateに渡すURLをurl('/shapes/:id')のように型付けされた形で指定できるようになる。
この型安全なアプローチが適用されたShapeSectionコンポーネントの例を見てみよう。
1export const ShapeSection = () => { 2 let [{params, query}, setState] = useRouteState(url('/shapes/:id')); 3 4 let setPosition = () => { 5 setState((state) => ({ 6 ...state, 7 query: { 8 x: Math.floor(100 * Math.random()), 9 y: Math.floor(100 * Math.random()), 10 }, 11 })); 12 }; 13 14 let resetPosition = () => { 15 setState({params}); // クエリパラメーターをリセットし、パスパラメーターは維持 16 }; 17 18 return ( 19 <main> 20 <h1>Shape {params.id}</h1> {/* パスパラメーターのidを使用 */} 21 <Shape x={query.x} y={query.y} n={params.id + 2}/> {/* クエリとパスパラメーターを使用 */} 22 <p> 23 <button onClick={setPosition}>Move</button>{' '} 24 <button onClick={resetPosition}>Reset</button> 25 </p> 26 </main> 27 ); 28};
ここでuseRouteStateは、URLのパスパラメーター(params)とクエリパラメーター(query)を別々に、しかし型安全な形で提供している。params.idはURLの/shapes/123のような部分から取得され、スキーマで定義した通り数値として扱われる。query.xやquery.yはURLの?x=...&y=...のような部分から取得される。
setPosition関数は、先ほどのAppコンポーネントと同様にsetStateを使ってqueryの状態を更新する。これによりURLのクエリパラメーターが変更され、図形の位置が変わる。resetPosition関数は、setState({params})とすることで、現在のパスパラメーター(params)はそのままに、クエリパラメーター(query)をすべて削除する。例えば、/shapes/123?x=10&y=20というURLからxとyの情報を消して/shapes/123に戻す、といった操作が可能になる。定義されたスキーマによってparamsやqueryの型が正確に推論されるため、開発者はそれぞれのパラメーターがどのようなデータ型を持つかを意識しやすく、誤った使い方を防ぎやすくなる。
このアプローチは、URLパラメーターという、アプリケーションの外部から制御される状態を、ReactのuseStateに似た直感的な方法で管理できるようにする。これにより、コンポーネント内部のローカルステートとURLパラメーターによるステート管理の間に一貫性が生まれ、コードがより理解しやすくなる。特に、createURLSchemaとzodを使った型安全なルーティングの導入は、URLの構造が複雑になった場合でも、開発者が安全に、そして効率的に作業を進めるための大きな助けとなるだろう。結果として、開発者はアプリケーションの状態管理をよりシンプルに捉え、バグの少ない、堅牢なWebアプリケーションを構築しやすくなる。