react-router-dom で ルーティングを実装する
React で ルーティングをする為のライブラリでデファクトスタンダードとなっているのが react-router です。 そして、Web 上で利用できるように DOM にバインディングができるライブラリが react-router-domです。
ちなみに、react-router と react-router-dom の違いが気になったのですが、この記事がとても参考になりました。
react-rourer-dom を追加する
react-rourer-dom と TypeScript を使用する為、型定義ファイルを追加します。
yarn add react-router-dom @types/react-router-dom
BrowserRouter でアプリケーションをラップする
History API を利用して URL と連動した React アプリケーションとなるように <BrowserRouter></BrowserRouter>
で全体をラップします。
ここでは、公式ドキュメントのクイックスタートを倣って BrowserRouter as Router
として Router
という別名で使用します。
// src/index.tsx import React from 'react'; import ReactDOM from 'react-dom'; import {BrowserRouter as Router} from 'react-router-dom' import App from './App'; ReactDOM.render( <Router> <App /> </Router>, document.getElementById("root") );
route を設定する
route の設定を行う為に react-router-dom から、Switch と Route と Redirect コンポーネントをインポートします。 それぞれの コンポーネント の役割がこちらです。
コンポーネント | 役割 |
---|---|
Switch | route の設定に応じてコンポーネントをレンダリングするエリア |
Route | パスに紐づくコンポーネントを定義する |
Redirect | リダイレクトを担うコンポーネント |
// src/App.tsx import React from 'react'; import {Switch, Route, Redirect} from 'react-router-dom' import Home from './components/Home' import About from "./components/About"; const App: React.FC = () => { return ( <div className="App"> <Switch> <Route path="/about"> <About /> </Route> <Route path="/"> <Home /> </Route> <Redirect to="/" /> </Switch> </div> ); } export default App;
設定したルーティングの内容で画面遷移を行う
実際に画面に遷移を行う為には、<a></a>
ではなく、<Link></Link>
コンポーネントを使用します。
// src/components/Home/index.tsx import React from 'react' import {Link} from 'react-router-dom' const Home: React.FC = () => ( <> <h1>This is Home</h1> <Link to="/about">About</Link> </> ); export default Home
動的なルーティング
動的なルーティングを行う場合は <Route path="/dynamic/:id"></Route>
のように :○○
とします。
import React from 'react'; import {Switch, Route, Redirect} from 'react-router-dom' import Home from './components/Home' import About from "./components/About"; import Dynamic from "./components/Dynamic"; const App: React.FC = () => { return ( <div className="App"> <Switch> <Route path="/dynamic/:id"> <Dynamic /> </Route> <Route path="/about"> <About /> </Route> <Route path="/"> <Home /> </Route> <Redirect to="/" /> </Switch> </div> ); } export default App;
/dynamic/:id
に紐づくコンポーネントはこのようにしています。
// src/components/Dynamic/index.tsx import React from 'react' import {useParams, Link} from 'react-router-dom' const Dynamic: React.FC = () => { const dynamicId = useParams<{id: string}>().id return ( <> <h1>This is Dynamic: {dynamicId}</h1> <Link to="/">Home</Link> </> ) } export default Dynamic
パスの :id
をコンポーネント側で取得する際は、useParams()
を使用します。
取得したいパラメータを useParams<{id: string}>()
のようにジェネリクスで指定します。
こちらの記事のように RouteComponentProps
という型定義をインポートして型結合する方法でも対応できます。
ライブラリの TypeScript の型定義ファイルを探す
予めライブラリ側で型定義ファイルが用意されている事もありますが、そうでない場合は DefinitelyTyped で探す事になります。 DefinitelyTyped は、OSS として個人が作成した型定義ファイルを集めているものです。ライブラリが公式に提供している型定義ファイルとは異なる為、完全に動作する保証はないです。
(それでも無い場合は、、、自作、、、、)
DefinitelyTyped の公式サイト上で探す
トップページ上部にある「TypeSearch」というリンクをクリック
遷移後の画面で探したい型定義ファイルのライブラリ名を入力します。
CLI で探す
npm info @types/○○
もしくは、
yarn info @types/○○
のように ○○ にライブラリ名をいれて叩けば型定義ファイルが提供されるいてるか調べる事ができます。
もしライブラリが公式に提供している場合は、上のコマンドを実行した際に「公式が提供しているので必要ない」といったメッセージが表示されます。 キャプチャは、DefinitelyTyped で axios の型定義を探した場合のものです。
React コンポーネントをカスタムフックで Presentational Component と Container Component に分離する
- Presentational Component(コンポーネント) と Container Component(コンテナー)
- カウンターコンポーネントを Presentational Component(コンポーネント) と Container Component(コンテナー)に分けて実装する
Presentational Component(コンポーネント) と Container Component(コンテナー)
コンポーネント を Presentational Component(コンポーネント) と Container Component(コンテナー) の2種類に分類する事で、再利用性を高める事ができる考え方です。 これは、Presentational and Container Components という記事で説明されています。
ただし、記事の冒頭にこのように記載されています。
Update from 2019: I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component. Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.
フックの登場によってコンポーネントをこのような考え方で分割する事はおすすめしないという事です。 せっかく学んだので記事にしますが...フックがある以上このように考える必要はないのかなと... 思っています。
それぞれのざっくりとした役割を私はこのように解釈しています。
Presentational Component(コンポーネント)
見た目を担うコンポーネント
Container Component(コンテナー)
Presentational Component(コンポーネント)または、Container Component(コンテナー)を内包してデータや動作を提供する
カウンターコンポーネントを Presentational Component(コンポーネント) と Container Component(コンテナー)に分けて実装する
カウンターコンポーネントの Presentational Component(コンポーネント)
import React from 'react'; interface CounterProps { count: number; increment: () => void; decrement: () => void; } const Counter: React.FC<CounterProps> = ({count, increment, decrement}) => { return ( <div> <div>Counter: {count}</div> <div> <button onClick={increment}>+1</button> <button onClick={decrement}>-1</button> </div> </div> ); }; export default Counter
カウンターコンポーネントの Container Component(コンテナー)
import React, {useState} from 'react' import CounterComponent from '../../components/Counter/Counter' const useCounter = (initialCount: number): [number, () => void, () => void] => { const [count, setCount] = useState(initialCount); const increment = () => { setCount(prevCount => prevCount + 1) } const decrement = () => { setCount(prevCount => prevCount - 1); }; return [count, increment, decrement]; }; const CounterContainer: React.FC = () => { const [count, increment, decrement] = useCounter(0) return <CounterComponent count={count} increment={increment} decrement={decrement} /> } export default CounterContainer
TypeScript の baseUrl について調べた事
Qiitaへ「TypeScript の baseUrl について調べた事」を投稿しました。 qiita.com
Qiita投稿 - Nuxt.js の generate で生成したページに 「/index.html」でアクセスすると JavaScript が実行されない問題
Qiitaへ「 Nuxt.js の generate で生成したページに 「/index.html」でアクセスすると JavaScript が実行されない問題」という記事を投稿しました。
manifest.json とは何か?
manifest.json とは
PWA として Web アプリケーションをインストールした時に、アプリケーションの振る舞い(アプリケーションの名前や説明、モバイル端末のホーム画面に表示するアイコンや、アドレスバーの色、スプラッシュスクリーンなど)を定義する JSON ファイルの事。
JSON ファイルではあるが、//
でコメント記述する事ができる。
manifest.json の構成
{ "short_name": "TODO", "name": "My TODO App", "icons": [ { "src": "/images/icons-192.png", "type": "image/png", "sizes": "192x192" }, { "src": "/images/icons-512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": "/todo/?source=pwa", "background_color": "#FFFFFF", "display": "standalone", "scope": "/todo/", "theme_color": "#FFFFFF" }
この他に様々なキーで振る舞いを定義をする事ができる。
詳細は、manifest.json - Mozilla | MDN を参照。
manifest.json の読み込み
HTMLの <head> ~ </head>
内において <link>
タグを用いて読み込む。
<link rel="manifest" href="/manifest.json">
「Nuxt + Lottie で お手軽アニメーション」というタイトルでLTをしました。
先日開催された、SaCSS vol.107 において「Nuxt + Lottie で お手軽アニメーション」というタイトルで LT をしました。
Nuxt.js の Assetsに格納されている画像を Vue 単一ファイルコンポーネントのdataプロパティで読み込む方法
Nuxt.js の Assetsに格納されている画像を Vue 単一ファイルコンポーネントのdataプロパティで読み込むには、require('@/assets/image/hoge.png')
といった具合に、require()
を使用する。
<template> <img :src="url" alt="" /> </template> <script> export default { data() { return { url: require('@/assets/image/hoge.png') } } } </script>