貼り付けた画像に枠と影を追加(はてなブログ改造計画)
記事中に貼り付けた画像に枠と影をつけようかな、と思ったので、今回はそう改造する方法です。
変更方法
記事中に貼り付けた画像を対象として枠と影をつけます。
変更手順
管理画面から以下のようにたどります。
デザイン > レンチマーク > デザインCSS
ここに追記をします。
追加CSS
.entry-content img { border: 1px #000 solid; box-shadow: 5px 5px 2px #ccc; }
変更結果
透過の有無に関わらず同じような表示になります。
説明
以降、設定した CSS の説明です。
指定箇所についてです。
.entry-content img {
.entry-content
が記事の内容。その配下の img
タグとしました。
ここは意外と悩ましいところでした。はてなブログの仕様が変わったのか、時期によって生成されているタグが違いましたね。。。もっと絞り込むクラスを入れても良いのかも知れませんが、この程度に留めました。
次に装飾の内容です。
border: 1px #000 solid;
box-shadow: 5px 5px 2px #ccc;
border: 1px #000 solid
: 1px の黒の実線。box-shadow: 5px 5px 2px #ccc
: 「右に5px 下に5px 2pxのぼかし グレー(#ccc
)」の影。
影は box-shadow
属性が有名ですが、最近は filter
属性に drop-shadow
を当てるのが流行りでしょうか。
/* (注意)うちのブログはこうなっていません。 */ filter: drop-shadow(5px 5px 2px #ccc);
こうすると、透過 PNG や SVG などで四角以外の画像でも上手く影を付けてくれるのが特徴です。(枠がついていてヤヤコシイですが、枠の中の表示だけ見てください。)この場合は border
を付けない方が良いと思います。
うちのブログでは、枠を付けたいので box-shadow
を使うことにしました。
drop-shadow
についてはこちらに。
尚、利用できるブラウザを調べるのには CanIUse を使うと良いでしょう。
強調文字に蛍光マーカーを引く(はてなブログ改造計画)
強調文字が太字だけだと目立ちっぷりが足りないので、蛍光マーカーを引いたような見た目に改造する方法です。
変更方法
エディタの「B」ボタンで強調した部分の装飾です。 Markdown で書くと **強調**
の部分に当たります。
変更手順
管理画面から以下のようにたどります。
デザイン > レンチマーク > デザインCSS
ここに追記をします。
追加CSS
.entry-content strong { background: linear-gradient(transparent 30%, #a6d9fe 30%); font-weight: bold; }
変更結果
説明
以降、設定した CSS の説明です。
指定箇所についてです。
.entry-content strong {
.entry-content
が記事の内容。その配下の strong
タグとしました。
次に装飾の内容。
background: linear-gradient(transparent 30%, #a6d9fe 30%); font-weight: bold;
ここは linear-gradient
という属性が分かりにくいのでその点を詳しく書きました。
linear-gradient
属性は、本来はグラデーションです。ですが、 開始色
と 終了色
にあえて同じ割合(パーセント)を指定すると、その指定割合(パーセント)の前後をベタ塗りできるというテクニックがありますので、これを使ってます。
linear-gradient
: グラデーションで徐々に色を変化させます。transparent 30%
: 開始色。上から 30% の高さまで透明色。それ以降はグラデーションという意味。#a6d9fe 30%
: 終了色。上から 30% の高さから青(`#a6d9fe
)色。それ以前はグラデーションという意味。
font-weight
:bold
: 太字
linear-gradient(方向, 開始色, 終了色)
になります。 方向
を書かないとデフォルトの「左から右」が適用されます。 linear-gradient(transparent, #a6d9fe)
であれば、透明から青色に徐々に変わります。補助として「半角スペース + 割合(パーセント)」を入れると、 開始色
の場合はそこまでベタ塗りでそれ以降がグラデーション、 終了色
の場合はそこからベタ塗りでそれ以前がグラデーション。
色を変えたり、割合を変えたり、他の属性を追加したりしてカスタマイズして取り入れると良いかと思います。
当サイトのURLが変更になりました。はてなブログの独自ドメイン化の話
URL が変わりました
当ブログのURLが変更になりました。
旧)https://hiranoon.hatenablog.com
新)https://multimineral-tech.com/
旧 URL は、当面は新URLにリダイレクトされます。しばらくはどちらからもアクセスできますが、しばらく後は新 URL のみとなります。
今後とも、何卒よろしくお願いいたします。
(余談)はてなブログの設定変更色々
んま、そゆことで、この後は余談です。
設定を色々いじってここまできました。今回の作業として実施したのは以下です。
- お名前.com でドメインを取得した。
- 「はてなブログPro」に加入した。
- お名前.comで DNS 関連機能の設定を追加した。
- はてなブログの設定画面に取得した独自ドメインを登録した。
- Google Analytics との連携を実施した。
- Google Search Console との連携を実施した。
はてな公式で結構詳しく説明してくれています。
公式だけで十分、ですが、お名前.com に慣れていない人は他のサイトも参考にする方が良いかもです。
Google 周りは Analytic と Search Console だけ設定しました。
Google Analytics はトラッキングIDと言われる「UA-XXXX」というコードをはてなブログに設定すればOK。が、そのトラッキングIDが Google Analytics のサイトのどこに出てくるか分からんかったですね。。。以下のサイトさんが参考になりました。
Google Search Console は旧URLから新URLへのアドレス変更ツールが用意されています。が、できませんでした。。。
はてなブログの独自ドメイン移行って、勝手に「302」リダイレクトをしてくれるみたいなんです。が、「301」リダイレクトはできないようです。詰みじゃん。
ま、いいか。
React with TypeScript プロジェクトの Material-UI でレイアウトを作成する
前回は Material-UI のデザインの変更を行いました。今回はその続きとして Theme をカスタマイズする元となるレイアウト作成を行います。ソースコードは前回の記事を踏まえていますのでその点ご了承ください。(といって、踏まえないでも読めてしまうように工夫はしておきます。)
それぞれの章は以下の流れで書いています。
- 全体のソースの掲載
- 部分部分の説明
「全体のソース」は長くなりがちですが、さっと流して読み進めていただければと思います。
シリーズ一覧
初期状態
前回やったこと、今回やること
前回は makeStyles
を利用して、独自 CSS を追加しました。また、勝手にデフォルトの Theme が使わることになるにも触れました。
今回は、管理画面(ダッシュボード)のレイアウトを構築します。 Theme のカスタマイズはこの次に。(カスタマイズする元画面を作るのを先にしました。)
変更前のソース
前回まで作った DashboardTemplate.tsx
から説明用の余計なモノを削り、シンプルな形に変更しておきました。
// /src/templates/DashboardTemplate.tsx import React from 'react'; import { Link } from 'react-router-dom'; import { makeStyles, Theme } from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container'; const useStyles = makeStyles((theme: Theme) => ({ link: { margin: theme.spacing(1, 1.5), }, })); export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { const classes = useStyles(); return ( <div> <AppBar> <Toolbar> <nav> <Link to="/" className={classes.link}> Top </Link> <Link to="/about" className={classes.link}> About </Link> </nav> </Toolbar> </AppBar> <Container> <h1>{title}</h1> <div>{children}</div> </Container> </div> ); }; export default DashboardTemplate;
見た目は以下です。 <Container>
内の <h1>
が、 <AppBar>
の下に潜って見えなくなっちゃってますが、こちらは追々修正します。
サイドメニューの追加
左サイドにメニューを用意し、 Top と About のリンクをこちらに移動させます。
使うのは <Drawer>
。これが左サイドメニューになります。(実際には上下左右どこにでも配置可能です。)
そして、その中に縦にリンクを配置するのに <List>
とその仲間( <ListItem>
, <ListItemIcon>
, <ListItemText>
)を使います。
この時点で <Link>
タグを削っています。一時的にコメントアウトしています。 Top と About の切り替えはまた後で追加します。まずはデザイン面をやっつけます。
// /src/templates/DashboardTemplate.tsx import React from 'react'; // import { Link } from 'react-router-dom'; // import { makeStyles, Theme } from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container'; import Drawer from '@material-ui/core/Drawer'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import HomeIcon from '@material-ui/icons/Home'; import InfoIcon from '@material-ui/icons/Info'; // const useStyles = makeStyles((theme: Theme) => ({ // link: { // margin: theme.spacing(1, 1.5), // }, // })); export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { // const classes = useStyles(); return ( <div> <AppBar> <Toolbar> {/* ココにあった <nav> を削除 */} </Toolbar> </AppBar> {/* ココから追加 */} <Drawer variant="permanent"> <List> <ListItem> <ListItemIcon> <HomeIcon /> </ListItemIcon> <ListItemText primary="Home" /> </ListItem> <ListItem> <ListItemIcon> <InfoIcon /> </ListItemIcon> <ListItemText primary="About" secondary="hogehoge" /> </ListItem> </List> </Drawer> {/* ココまで追加 */} <Container> <h1>{title}</h1> <div>{children}</div> </Container> </div> ); }; export default DashboardTemplate;
以下のような表示になりました。
Drawer の追加
上下左右に配置できる領域です。本来は、引き出しのようにスライドして表示・非表示が切り替わります。(下記リンク先の「Temporary drawer」の項の「LEFT RIGHT TOP BOTTOM」の4つのボタンを押すとどんなものか分かると思います。)
表示しっ放しにすることも可能です。すると単なるサイドバーになります。
<Drawer variant="permanent"> {/* 中略 */} </Drawer>
<Drawer>
には variant
という属性があります。属性値は以下のようになってます。(公式の説明は私は一読では分からんかったので、私に分かるように書いてみた。。。)
permanent
: ずっと表示されたまま。persistent
: 表示・非表示の切り替えができる。表示と非表示の機能(ボタン等)はそれぞれ自作する必要がある。temporary
: 表示・非表示の切り替えができる。基本は非表示なのが特徴。表示機能(ボタン等)は自作が必要だが、<Drawer>
の領域外をクリックすると閉じるので、閉じる機能は作らなくてもOK。
persistent
と temporary
の違いが分かりにくいんですよね。 persistent
は一度表示したら表示しっぱなし(永続的)、 temporary
は別のところをクリックすると引っ込む(一時的)ということです。
今回は permanent
なので、非表示になることがありません。
List の追加
縦方向に並べる索引が Lists です。
<List> <ListItem> <ListItemIcon> <HomeIcon /> </ListItemIcon> <ListItemText primary="Home" /> </ListItem> <ListItem> <ListItemIcon> <InfoIcon /> </ListItemIcon> <ListItemText primary="About" secondary="hogehoge" /> </ListItem> </List>
典型的な構造は以下のようになります。(モチロン他のパターンもある。)
<List>
<ListItem>
<ListItemIcon>
<ListItemText>
<ListItem>
- ...
<ListItem>
- ...
- ...
HTML でいうと、 <List>
が <ul>
、 <ListItem>
が <li>
にあたります。実際にそう変換されてブラウザに出力されます。
その <li>
にあたる <ListItem>
の中には、 <ListItemIcon>
、 <ListItemText>
といった専用の部品を入れる前提で考えると良さそうです。先のリンク先には他の部品も使った例がありますので、好みで利用すると良いです。
ListItemText
<ListItemText>
の属性に primary="Home"
とあります。 <li>
のメインテキストを指します。 secondary
は <li>
のサブテキストになります。2種類の文字を設定できる仕掛けになってます。
例えば、 <ListItemText primary="About" secondary="hogehoge" />
とすると以下のような見た目になります。(あくまで例です。今回は secondary
は使いません。)
ちなみに、私は最初に <ListItemText primary="hoge">
のように primary
だけ使われているサンプルコードを見て意味が理解できませんでした。メインとサブのテキストが設定できる仕組みになってて、そのメインだけ設定しているという意味だったんですね。詳しくは以下にあります。
ListItemIcon
Material-UI には Material Icons というものがあります。
任意で好きなアイコンが使えます。使いたいアイコンをクリックすると、使うべきコードも表示されます。
<ListItemIcon>
はコンテントとして Material Icons が使われる想定になっています。 <ListItemIcon><HomeIcon /></ListItemIcon>
のように書くだけなので、これは直感的で分かりやすいですよね。
Drawer の開閉機能の設置
続いて <Drawer>
の開閉機能を設置します。
開く機能を持ったボタン(いわゆるハンバーガーメニューというヤツ)を <Toolbar>
に付けるようにします。 <IconButton>
というボタンです。
閉じる機能は <Drawer>
自身のモノを使います。 variant="temporary"
にして領域外をクリックしたら閉じるというヤツです。
開閉の状態は React Hooks で管理します。 drawerOpen
という変数( state )を用意しています。
import React from 'react'; // import { Link } from 'react-router-dom'; // import { makeStyles, Theme } from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container'; import Drawer from '@material-ui/core/Drawer'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import HomeIcon from '@material-ui/icons/Home'; import InfoIcon from '@material-ui/icons/Info'; import MenuIcon from '@material-ui/icons/Menu'; import IconButton from '@material-ui/core/IconButton'; // 追加 // const useStyles = makeStyles((theme: Theme) => ({ // link: { // margin: theme.spacing(1, 1.5), // }, // })); export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { // const classes = useStyles(); // 追加: Drawer の開閉状態(フックを利用) const [drawerOpen, setDrawerOpen] = React.useState(false); // 追加: Drawer の開閉 const handleDrawerToggle = () => { setDrawerOpen(!drawerOpen); // Drawer の開閉状態を反転 }; return ( <div> <AppBar> <Toolbar> {/* 追加 */} <IconButton color="inherit" onClick={handleDrawerToggle}> <MenuIcon /> </IconButton> </Toolbar> </AppBar> {/* 属性を変更 */} <Drawer variant="temporary" open={drawerOpen} onClose={handleDrawerToggle} > <List> <ListItem> <ListItemIcon> <HomeIcon /> </ListItemIcon> <ListItemText primary="Home" /> </ListItem> <ListItem> <ListItemIcon> <InfoIcon /> </ListItemIcon> <ListItemText primary="About" /> </ListItem> </List> </Drawer> <Container> <h1>{title}</h1> <div>{children}</div> </Container> </div> ); }; export default DashboardTemplate;
以下のような表示になりました。
React Hooks による開閉状態の保持
以下のコードを追加しました。 drawerOpen
という変数(以降 state と呼ぶ)に開閉状態を保持します。 handleDrawerToggle
は drawerOpen
の true/false
を反転させています。
// 追加: Drawer の開閉状態(フックを利用) const [drawerOpen, setDrawerOpen] = React.useState(false); // 追加: Drawer の開閉 const handleDrawerToggle = () => { setDrawerOpen(!drawerOpen); // Drawer の開閉状態を反転 };
以下、 React Hooks を簡単に説明しておきます。
React.useState(状態の初期値);
という関数は、「状態(の変数)」と「状態をセットする関数」を配列で返却してくれます。これをフック (Hook) と呼びます。 const [状態, 状態をセットする関数] = React.useState(状態の初期値);
のように書いて使います。
「状態」である drawerOpen
は、最初は false(状態の初期値)
になります。また false
をセットしているので drawerOpen
の型は boolean であると決まりました。
「状態をセットする関数」である setDrawerOpen
は、 setDrawerOpen(true)
や setDrawerOpen(false)
のようにして使います。これで drawerOpen
の値を変更します。( drawerOpen = true;
のような書き方はできません!) setDrawerOpen(!drawerOpen);
は drawerOpen
の逆(!
は boolean の反転)をセットしていますから true/false
が反転することを意味します。
この反転を行う処理を handleDrawerToggle
という関数で用意しています。
React Hooks は公式サイトでも分かりやすく説明されています。
Drawer の属性を変更
<Drawer>
の属性を変更しました。 <Drawer>
が開閉する temporary
に変更し、閉じた時の state の変更処理を加えます。
{/* 属性を変更 */} <Drawer variant="temporary" open={drawerOpen} onClose={handleDrawerToggle} >
variant
:temporary
にして表示・非表示の切り替えができるようにします。領域外をクリックすると閉じます。open
:true
で開いてfalse
で閉じます。onClose
: 閉じた時に呼び出される関数です。
temporary
にたので領域外をクリックすると閉じます。このときに onClose
で指定した関数を呼び出します。
drawerOpen
が true
であった場合に領域外をクリックすると、以下のような動きになります。
drawerOpen
はtrue
。open
属性がtrue(=drawerOpen)
であるため<Drawer>
は開いている。- 領域外をクリックする。
onClose
属性で定義したhandleDrawerToggle
関数が呼び出される。handleDrawerToggle
関数はdrawerOpen
を反転させるのでdrawerOpen
がfalse
になる。open
属性がfalse
になるので<Drawer>
が閉じる。
IconButton の追加
<Toolbar>
に <IconButton>
を追加しました。ボタンのアイコンは <MenuIcon />
を使います。
<AppBar> <Toolbar> {/* 追加 */} <IconButton color="inherit" onClick={handleDrawerToggle}> <MenuIcon /> </IconButton> </Toolbar> </AppBar>
onClick={handleDrawerToggle}
を見て分かると思いますが、クリックすると handleDrawerToggle
関数が起動して drawerOpen
が反転します。その結果 <Drawer>
が開くという仕掛けになります。
次は Drawer をレスポンシブにしたいと思います。
React with TypeScript プロジェクトの Material-UI でデザインをカスタマイズする
前回は Material-UI のコンポーネントを利用するところまでやりました。今回はその続きになります。ソースコードも前回の記事を踏まえていますのでその点ご了承ください。(といって、踏まえないでも読めてしまうとは思いますが。)
さて、今回あ Theme をいじったり Style をいじったりします。その方法は色々あるのですが、今回はその一例となります。なるべく今現在の主流と思われるやり方、といいますか、公式で使われている方法を採用しています。
シリーズ一覧
Theme という概念
説明をするその前に、 Material-UI には Theme という概念があります。(「Theming」と呼ばれているようです。)
よくあるテーマってやつだね、となんとなく察するかも知れませんが、そうです。 Material-UI のテーマとはどんなんであるのか、今回の前提知識として少し説明をしておきます。
- Theme により色や配置などのデザインが決まる。
- 何もしなくてもデフォルトの Theme が適用される。
- 色は Palette に定義されている。
- Palette には
Primary
,Error
,Warning
などのキーワードがあり、それぞれに色が定義されている。 - 色を指定するときは、このキーワードを利用する。
- デザインの変更は、基本はこの Theme や Palette を変更する
混乱しやすい部分は、何もしなくてもデフォルトの Theme が適用される点かなと思います。なので、 Theme を使ってなくても Theme を意識する必要があるので注意です。(本格的に利用するなら Theme をいじらないことってないんでしょうけど、利用しはじめの時はピンとこないかな、と思います。)
Theme と Palette については以上ですが、少しだけ説明を加えておきます。
Theme について
前回の記事では、 AppBar
というコンポーネントを使ったら何も設定していないのに「青色」になってました。
Theme では「色や配置」などのデザインが定義されています。 AppBar
のような Material-UI のコンポーネントには、 Theme で定義された色やデザインになる仕組みになっています。 AppBar
が「青色」になっていたのは、 Theme の色が適用されていた訳です。
Theme はカスタマイズが可能です。ですが、特に何もしなかった場合はデフォルトの Theme が適用されます。先程の「青色」はデフォルトの色だった訳です。
公式サイトの Theme はこちらです。
Palette について
では、 Theme の色は何になっているのでしょうか? これは Palette というもので定義されています。 Primary
, Secondary
, Error
, Warning
, Info
, Success
という区分けでそれぞれ色が定義されています。
AppBar
に色を指定しないと Primary
になります。 Primary
はデフォルトで青色です。こういう理屈で青色のバーが上部に表示されていました。
公式サイトの Palette はこちらです。
makeStyles で Style を追加する
さて、CSS でデザインをしていきましょう。
/src/templates/DashboardTemplate.tsx
を変更します。内容の全貌は以下の通りです。
// /src/templates/DashboardTemplate.tsx import React from 'react'; import { Link } from 'react-router-dom'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container'; // (2) 追加 import { makeStyles, Theme, } from '@material-ui/core/styles'; // (2) useStyles を追加 const useStyles = makeStyles( (theme: Theme) => ({ toolbar: { fontWeight: 'bold', }, link: { margin: theme.spacing(1, 1.5), }, }) ); export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { // (2) classes を追加 const classes = useStyles(); return ( <div> {/* (1) それぞれに属性を追加 */} <AppBar position="static" color="default"> <Toolbar className={classes.toolbar}> <nav> <Link to="/" className={classes.link}> Top </Link> <Link to="/about" className={classes.link}> About </Link> </nav> </Toolbar> </AppBar> <Container> <h1>{title}</h1> <div>{children}</div> </Container> </div> ); }; export default DashboardTemplate;
(1) 属性の追加
AppBar
以下のタグに属性を追加しています。
{/* (1) それぞれに属性を追加 */} <AppBar position="static" color="default"> <Toolbar className={classes.toolbar}> <nav> <Link to="/" className={classes.link}> Top </Link> <Link to="/about" className={classes.link}> About </Link> </nav> </Toolbar> </AppBar>
それぞれの設定内容について説明します。
AppBar
position="static"
: 表示位置設定です。 CSS の position と同じモノが属性として設定できます。デフォルトはfixed
ですのでstatic
に変更しています。color="default"
: 色です。デフォルトは Theme のPrimary
が設定されます。default
を指定するとグレー表示になります。
Toolbar
className={classes.toolbar}
: 後述する独自 CSS の追加です。
Link
className={classes.link}
: 後述する独自 CSS の追加です。
AppBar
の position
と color
は AppBar
固有の属性です。公式のサイトにも利用方法が書かれています。
Link
の className
は React の属性で HTML の class
に相当します。 Material-UI では属性値の {classes.toolbar}
の方が重要で、次項ではコレについて説明します。
(2) 独自 CSS の追加
makeStyles
という機能で独自 CSS を追加しています。この部分に関して話をします。
// (2) useStyles を追加 const useStyles = makeStyles( (theme: Theme) => ({ toolbar: { fontWeight: 'bold', }, link: { margin: theme.spacing(1, 1.5), }, }) ); // ...中略... const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { // (2) classes を追加 const classes = useStyles(); // ...中略... };
const useStyles = makeStyles(() => ({ ... });
で useStyles
という関数を作っています。 makeStyles
は引数に関数を取ります。この関数の返却値( ({ ... })
の部分にあたる)に JSON 形式で CSS を記述します。
できた useStyles
は React のコンポーネント内で利用できます。 const classes = useStyles();
のようにして受け取り、 <Link to="/" className={classes.link}>
のように className
属性に指定ができます。
makeStyles
に独自 CSS を記述する。makeStyles
の結果(=useStyles
)を React のコンポーネント内で受け取る。- タグの中の
className
属性の値で利用する。
独自 CSS の追加と利用
makeStyles(() => ( ... ));
の ...
の部分に JSON で書くだけです。属性は CSS のようなハイフンつなぎではなくキャメルケースになることに注意です。以下のように定義します。
const useStyles = makeStyles( () => ({ toolbar: { fontWeight: 'bold', }, }) );
利用も難しくはないと思います。
const classes = useStyles(); return ( <Toolbar className={classes.toolbar}> );
こんな感じで toolbar
で定義した fontWeight: 'bold'
が適用されます。
Theme の値を利用した CSS の追加
先程は 'bold'
という属性値を追加しました。この属性値の設定に Theme の値を使うことも可能です。例えば、 border
の色に Theme の Primary の色を使いたい、みたいなことです。
今回は、リンク文字のスペースに theme.spacing()
という関数を使います。
Theme の spacing
Theme には spacing
という値が定義されています。余白に利用する基本的な数字です。デフォルトの Theme だと 8px
になっているようです。
リンク文字の余白として、 CSS の margin
を追加したいと思います。 theme.spacing()
という関数を theme.spacing(1, 1.5),
のよう使うと以下のような意味になります。
margin: 8px, 12px;
8px * 1, 8px * 1.5
が展開されて、上記のような CSS として扱われます。(ちなみに、 theme.spacing(1, 2, 3, 4),
なら 8px, 16px, 24px, 32px;
になります。)
theme.spacing()
については公式サイトの以下に説明があります。
theme.spacing を使った CSS の追加と利用
Theme を使うときは makeStyles
の引数が必要になります。 theme
という引数(型は Theme
なので theme: Theme
と書いている)を指定すると、現在適用される Theme を利用されることになります。 theme
をどこかで定義する必要はありません。
const useStyles = makeStyles( (theme: Theme) => ({ link: { margin: theme.spacing(1, 1.5), }, }) );
利用は難しくはないと思います。
const classes = useStyles(); return ( <Link to="/about" className={classes.link}> About </Link> );
こんな感じで link
で定義した margin: theme.spacing(1, 1.5),
が適用されます。最終的に margin: 8px, 12px;
という CSS として出力されます。
対応結果
以上にて、以下のような表示に変わりました。
次は Theme を編集する話をしていきたいと思います。
React with TypeScript プロジェクトに Material-UI を導入してみる
React with TypeScript のプロジェクトにて、 Material-UI を使い始めるまでの方法について書いていきます。
簡単な管理画面(ダッシュボード)を作成して完了としたいのですが、当該記事では Material-UI を利用する環境を用意して終えることにします。利用方法については次の記事に任せる構成にします。
シリーズ一覧
前提事項
事前準備
以下の記事で書きました、 React を TypeScript で利用する環境を作成済であることを前提とします。
この記事中の内容は、上記作業を終えた上で行っているモノと読んでください。
バージョン情報
今回の記事は以下のバージョンで行ったことを前提としています。バージョン違いで記事の内容と異なる場合がありますので、その点はご了承ください。
主要なライブラリは以下になります。
ライブラリ | バージョン |
---|---|
material-ui | ^4.11.2 |
react | ^17.0.1 |
react-router-dom | ^5.2.0 |
typescript | ^4.1.3 |
参考までに、今回の記事に関係ないライブラリも含めた package.json
も掲載しておきます。
// package.json { // 中略 "dependencies": { "@material-ui/core": "^4.11.2", "@material-ui/icons": "^4.11.2", "@testing-library/jest-dom": "^5.11.6", "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.6.0", "@types/jest": "^26.0.19", "@types/node": "^12.19.11", "@types/react": "^16.14.2", "@types/react-dom": "^16.9.10", "react": "^17.0.1", "react-dom": "^17.0.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", "typescript": "^4.1.3", "web-vitals": "^0.2.4" }, // 中略 "devDependencies": { "@types/react-router-dom": "^5.1.7", "@typescript-eslint/eslint-plugin": "^4.11.0", "@typescript-eslint/parser": "^4.11.0", "eslint": "^7.16.0", "eslint-config-airbnb": "^18.2.1", "eslint-config-prettier": "^7.1.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.3.0", "eslint-plugin-react": "^7.21.5", "eslint-plugin-react-hooks": "^4.2.0", "prettier": "^2.2.1" } }
Material-UI のインストール
Material-UI のインストールです。
こちらの手順にある内容をまず実施しておきます。
npm
でインストールするのは以下の2つ。
$ npm install --save @material-ui/core $ npm install --save @material-ui/icons
フォントは HTML ファイルに追加することにします。
<!-- ./public/index.html --> <!-- 前略 --> <head> <!-- 中略 --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> <!-- 中略 --> </head> <!-- 後略 -->
例えば、 Material-UI の Date Picker のような部品を利用したいとなったなら @material-ui/pickers
もインストールする必要があります。こういった部品(Button や DatePicker など)に関しては公式サイトに解説のページがあります。そしてそのページ内で追加でインストールすべきモノが説明されています。ですので、今後部品が必要になった都度インストールすればOKかと思います。
ということで、今回は一旦ここまで。あとは必要に応じて。
簡単な画面の作成(Material-UI 利用前)
続いて、簡単な画面を作成することにします。
今回はめっちゃ簡素なダッシュボードのようなものを作ります。「Top ページ」と「About ページ」いう2ページのみの構成とし、互いに行き来ができるようにします。ダッシュボードという名の「テンプレート」を用意し、「Top ページ」と「About ページ」は、この「テンプレート」の中に表示されるようにします。
ディレクトリ・ファイル構成
「テンプレート」と「Top ページ」と「About ページ」に対応したファイルを追加します。ディレクトリ構成は好みが分かれるところでしょうが、取り敢えず今回は下記のような形にします。
./src │ App.css │ App.test.tsx │ App.tsx │ index.css │ index.tsx │ logo.svg │ react-app-env.d.ts │ reportWebVitals.ts │ setupTests.ts │ ├─assets ### <-ADD 画像などの静的ファイルを入れる。 ├─components ### <-ADD 部品置き場。<HogeButton /> みたいな独自タグを作っていく。 ├─consts ### <-ADD 定数置き場。 ├─pages ### <-ADD 画面置き場。 │ AboutPage.tsx ### <-ADD About ページ │ TopPage.tsx ### <-ADD Top ページ │ └─templates ### <-ADD テンプレート置き場。 DashboardTemplate.tsx ### <-ADD ダッシュボードのテンプレート
ということで、ディレクトリとファイルを追加します。
$ mkdir src/assets $ mkdir src/components $ mkdir src/consts $ mkdir src/pages $ mkdir src/templates $ touch src/pages AboutPage.tsx $ touch src/pages TopPage.tsx $ touch src/templates DashboardTemplate.tsx
ページの作成
「Top ページ」と「About ページ」を作ります。最初は超単純にしておきましょう。
// /src/pages/TopPage.tsx import React from 'react'; const TopPage: React.FC = () => { return <>トップページの内容</>; }; export default TopPage;
// /src/pages/AboutPage.tsx import React from 'react'; const AboutPage: React.FC = () => { return <>アバウトページの内容</>; }; export default AboutPage;
ルーティング
次に、作成したページを表示させるためのルーティングを作っていきます。
ページを遷移させるルーティングの機能には react-router-dom
を利用します。 TypeScript を使っていますので、型もインストールします。型は開発環境でしか使いませんので --save-dev
オプションにしています。
$ npm install --save react-router-dom $ npm install --save-dev @types/react-router-dom
src/App.tsx
がアプリケーションの入り口になります。ここにルーティングを書くことになります。以下のように編集します。
// src/App.tsx import React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import AboutPage from './pages/AboutPage'; import TopPage from './pages/TopPage'; const App: React.FC = () => { return ( <Router> <Switch> <Route path="/" component={TopPage} exact /> <Route path="/about" component={AboutPage} exact /> </Switch> </Router> ); }; export default App;
これで、 http://localhost:3000/
は「Top ページ」へ、 http://localhost:3000/about
は「About ページ」に遷移します。
ページ遷移の <a>
タグの設置には <Link>
を使います。<Link to="/about">About</Link>
のように書くと、 AboutPage
へのリンクが設置されます。
今回はルーティングが本題ではありませんので、解説は省きます。ただ、今回は公式サイトの「Quick Start」に書かれているレベルのことしかしていませんので、知りたい方は下記を見ていただければいいかな、と。
テンプレートの作成
次に、「テンプレート」を作ってみましょう。まずは簡単な構成にします。
// /src/templates/DashboardTemplate.tsx import React from 'react'; export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { return ( <div> <h1>{title}</h1> <div>{children}</div> </div> ); }; export default DashboardTemplate;
呼び出し元には title
という属性を指定してもらいます。これを h1
で表示します。
呼び出し元の内容は、 children
になります。ここは取り敢えず div
で囲って表示します。( <>
でもいいのかもしれません。ですが、確実にブロックレベルになるように div
っていた方がトラブらないかな? と思ってこうしてます。)
「呼び出し元ではどう使うんだ?」というのが分からないとピンと来ないとは思いますので、この辺りは後述します。
title
と children
は DashboardTemplate
のプロパティ(props
)となりますので、 DashboardTemplateProps
という interface
を作りました。これを DashboardTemplate: React.FC<DashboardTemplateProps>
のように型指定します。
テンプレートの利用
それでは作成した「テンプレート」を「Top ページ」と「About ページ」で利用してみましょう。
// /src/pages/TopPage.tsx import React from 'react'; import DashboardTemplate from '../templates/DashboardTemplate'; // <=ADD const TopPage: React.FC = () => { // DashboardTemplate で囲うよう変更 return ( <DashboardTemplate title="トップページのタイトル"> <>トップページの内容</> </DashboardTemplate> ); }; export default TopPage;
// /src/pages/AboutPage.tsx import React from 'react'; import DashboardTemplate from '../templates/DashboardTemplate'; // <=ADD const AboutPage: React.FC = () => { // DashboardTemplate で囲うよう変更 return ( <DashboardTemplate title="アバウトページのタイトル"> <>アバウトページの内容</> </DashboardTemplate> ); }; export default AboutPage;
DashboardTemplate
タグで囲うように変更しました。
DashboardTemplate
タグには title="トップページのタイトル"
という属性を記述しています。これが <h1>{title}</h1>
の {title}
として使われます。
DashboardTemplate
タグの中身の <>アバウトページの内容</>
が <div>{children}</div>
の {children}
として使われます。
結果として、以下のような HTML として出力されます。( <>...</>
は React の便宜上書いている意味無しタグなので、HTML出力時には無視されます。)
<div> <h1>トップページのタイトル</h1> <div>トップページの内容</div> </div>
ということで、 Chrome に表示されたイメージと、出力されたHTMLは以下の画像のようになりました。
Material-UI の簡単な利用
ここまでで Material-UI を利用するための土台ができあがりました。次は「テンプレート」で Material-UI のコンポーネントを利用してみたいと思います。
今回は「テンプレート」の変更を行います。画面上部のヘッダーとして AppBar
を追加し、そこに2つのページに遷移するリンクを設置します。
ヘッダーの追加
/src/templates/DashboardTemplate.tsx
を変更します。内容の全貌は以下の通りです。
// /src/templates/DashboardTemplate.tsx import React from 'react'; // 以下の import を追加 import { Link } from 'react-router-dom'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container'; export interface DashboardTemplateProps { children: React.ReactNode; title: string; } const DashboardTemplate: React.FC<DashboardTemplateProps> = ({ children, title, }) => { return ( <div> {/* (1) <AppBar>...</AppBar> を追加 */} <AppBar> <Toolbar> <nav> <Link to="/">Top</Link> <Link to="/about">About</Link> </nav> </Toolbar> </AppBar> {/* (2) <div> を <Container> に変更 */} <Container> <h1>{title}</h1> <div>{children}</div> </Container> </div> ); }; export default DashboardTemplate;
(1) AppBar の追加と Container への変更
{/* (1) <AppBar>...</AppBar> を追加 */}
にある AppBar
の追加についてです。
{/* (1) <AppBar>...</AppBar> を追加 */} <AppBar> <Toolbar> <nav> <Link to="/">Top</Link> <Link to="/about">About</Link> </nav> </Toolbar> </AppBar>
AppBar
はヘッダーを作るモノ、 Toolbar
はその中で要素を横に並べるモノになります。組み合わせて利用します。(※1)
ナビゲーションのリンクなので一応 <nav>
を書いて、その中に <Link>
タグでリンク(<a>
として出力される)を配置しました。
AppBar についての Material-UI の公式サイトは下記になります。
※1. AppBar
と Toolbar
の違いはなんじゃろと思ってましたが、同じ疑問が下記にて解消されていました。
(2) Container への変更
続いて、 {/* (2) <div> を <Container> に変更 */}
にある Container
の利用についてです。
{/* (2) <div> を <Container> に変更 */} <Container> <h1>{title}</h1> <div>{children}</div> </Container>
これは div
を Container
に変更しただけです。 Container
は中央寄せされる div
というくらいに思ってください。
これで以下のように表示されます。
Top と About のリンクで画面がカチカチ切り替わります。 <h1>
の部分が隠れちゃったのと、リンク部分がちょっと窮屈なので、その点の改善を少しやっていきたいと思います。
が! 流石に長くなりそうなので次の記事にて話を続けます。
React で TypeScript を使う環境を構築する手順(いつもどおり VSCode に ESLint と Prettier を添えます。)
React の開発環境を作る。 JavaScript ではなく TypeScript を使いたい。という場合の環境構築手順メモです。利用するのは VSCode 。 ESLint と Prettier も使ってコードのチェックやフォーマットを実行してくれるようにしておきました。
- React
- TypeScript
- ESLint
- Prettier
- VSCode
以前、下記のような投稿をしました。こちらは React ではなかったのです。今回は下記の React 版になりますね。
今回は最小の設定、最小の構成を目指して環境を作りました。ここから各人の好みで調整しやすい構成にしたつもりです。参考になれば幸いです。
また、バージョンの違いによってはうまく行かないこともあるでしょう。実行時期により導入されるバージョンが変わってくると思いますので、そのあたりは適宜対応いただきたいです。(別のプロジェクトではうまく行ってたのが今回はエラーが消えなかったり、と、結構苦労しました。。。設定ミスかと思って調べたらバージョン違い(=作成時期の違い)に依るモノだった。。。)現時点でうまく行った例になりますが、その内容を漏らさず記載したつもりです。
環境情報(執筆時点)
執筆時点でのバージョンの情報を掲載しておきます。同一バージョンであれば同じ結果になる、はず。
手順開始前の環境
以下のような環境を前提とします。
種類 | バージョン |
---|---|
windows | 10 Pro |
node | v14.15.3 |
npm | 6.14.9 |
VSCode | 1.52.1 |
VSCode Extension / ESLint | v2.1.14 |
VSCode Extension / Prettier - Code formatter | v5.8.0 |
GitBash (GNU bash) | version 4.4.23(1)-release |
このあたりのツールの導入は過去記事を参照してもらえるとありがたいです。
手順実施後の結果
前述の環境を前提にして、当記事の作業(つまりこの後の文章にある作業)を行った最終結果を参考までに掲載しておきます。
バージョン情報は ./package.json
に記載されますので、そちらの一部を抜粋して掲載します。
// package.json { // 中略 "dependencies": { "@testing-library/jest-dom": "^5.11.6", "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.6.0", "@types/jest": "^26.0.19", "@types/node": "^12.19.11", "@types/react": "^16.14.2", "@types/react-dom": "^16.9.10", "react": "^17.0.1", "react-dom": "^17.0.1", "react-scripts": "4.0.1", "typescript": "^4.1.3", "web-vitals": "^0.2.4" }, // 中略 "devDependencies": { "@typescript-eslint/eslint-plugin": "^4.11.0", "@typescript-eslint/parser": "^4.11.0", "eslint": "^7.16.0", "eslint-config-airbnb": "^18.2.1", "eslint-config-prettier": "^7.1.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.3.0", "eslint-plugin-react": "^7.21.5", "eslint-plugin-react-hooks": "^4.2.0", "prettier": "^2.2.1" } }
React with TypeScript の準備
React の環境は create-react-app
を使って構築することにします。
create-react-app の実行
npx create-react-app (任意のプロジェクト名) --template typescript
で TypeScript を使った React プロジェクトが作成できます。
私の環境では npx create-react-app
だけだとうまく動かなかったので、グローバルに create-react-app
をインストールしてから実行しました。 create-react-app
のインストールはしなくて済むならそれに越したことはないと思いますので、飛ばせるなら飛ばしちゃってください。
### 下記コマンドは最初は実行しないで試してみましょう。 $ npm install -g create-react-app C:\Program Files (x86)\Nodist\bin\create-react-app -> C:\Program Files (x86)\Nodist\bin\node_modules\create-react-app\index.js + create-react-app@4.0.1 added 67 packages from 25 contributors in 5.529s $ create-react-app --version 4.0.1 ### 最初は下記コマンドからスタートしましょう。 ### もしエラーになるようなら「npm install -g create-react-app」を実行してから ### 再び下記コマンドを試してみましょう。 $ npx create-react-app sample-project --template typescript
任意ではありますが、以下のコマンドで実行を確認してもよいでしょう。ブラウザが立ち上がると思います。
$ cd sample-project $ npm start
補足 : create-react-app のオプション
ちょっと古い記事だと npx create-react-app (任意のプロジェクト名) --typescript
というオプションを使っている説明があります。今は公式サイトにて --template typescript
を使うように指定されているので、こちらを使いましょう。
ちなみに、公式サイトでは npm install -g create-react-app
は止めとけと書かれています。。。いや、やらずに動くんならやりたくないんですが、まあ、動かないのはしょうがないので私の環境ではやっちゃってます。
あと、「 npm install --save typescript @types/node @types/react @types/react-dom @types/jest
というコマンドも実行しとけ」と書かれていますが、特に実行しなくてもこれらは導入されますね。私は実行しませんでしたが、ちゃんと入っているか package.json
の確認はしておいて良いかも知れませんね。
ESLint の導入と設定
続いて ESLint を導入し、設定を行いましょう。
VSCode で開く
[ファイル] > [フォルダーを開く] より、作成したプロジェクトのフォルダーを指定して開きます。(今回の例だと「sample-project」フォルダーです。)(エクスプローラで右クリックから「Code で開く」でも良し。)
その後、 ctrl + @
などで、下部にコンソールを開いておくと良いです。
ESLint の導入
ESLint は npx eslint --init
で導入できます。プロジェクトの直下にてこのコマンドを入力し、各種設定を選択します。
選択した内容についてはインラインで記載していますが、「airbnb」の選択と「設定ファイルを JavaScript とする」部分の選択は好みでよいと思います。その2つ以外は下記の通りになっちゃうと思います。
$ npx eslint --init ### To check syntax, find problems, and enforce code style を選択 √ How would you like to use ESLint? · style ### JavaScript modules (import/export) を選択 √ What type of modules does your project use? · esm ### React を選択 √ Which framework does your project use? · react ### Yes を選択 √ Does your project use TypeScript? · No / Yes ### Browser を選択 √ Where does your code run? · browser ### Use a popular style guide を選択 √ How would you like to define a style for your project? · guide ### Airbnb: https://github.com/airbnb/javascript を選択 √ Which style guide do you want to follow? · airbnb ### JavaScript を選択 √ What format do you want your config file to be in? · JavaScript ...(中略)... ### Yes を選択 √ Would you like to install them now with npm? · No / Yes ...(中略)... + eslint-config-airbnb@18.2.1 + eslint-plugin-jsx-a11y@6.4.1 + eslint-plugin-react@7.21.5 + eslint-plugin-react-hooks@4.2.0 + eslint-plugin-import@2.22.1 + eslint@7.16.0 + @typescript-eslint/parser@4.11.0 + @typescript-eslint/eslint-plugin@4.11.0 added 2 packages from 3 contributors, updated 7 packages and audited 1953 packages in 14.141s ...(中略)... ESLint was installed locally. We recommend using this local copy instead of your globally-installed copy.
補足 : ESLint 関連プラグインの導入
他の記事によっては以下のコマンドを打つべしってありましたが、2020/12現在では不要みたいです。 npx eslint --init
で一緒にインストールされましたね。まあ、やっても問題にはならないとおもいますが。
$ yarn add -D eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import
ESLint の設定
React, TypeScript の環境にあった内容に変更します。
// .eslintrc.js module.exports = { env: { browser: true, es2021: true, jest: true, // <=ADD }, extends: [ 'plugin:react/recommended', 'airbnb', ], parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 12, sourceType: 'module', }, plugins: [ 'react', '@typescript-eslint', ], rules: { // ↓↓↓ADD↓↓↓ 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': ['error'], '@typescript-eslint/no-unused-vars': 'error', 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }], 'import/extensions': [ 'error', { extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] }, ], 'react/prop-types': 'off', 'spaced-comment': ['error', 'always', { markers: ['/ <reference'] }], // ↑↑↑ADD↑↑↑ }, // ↓↓↓ADD↓↓↓ settings: { 'import/resolver': { node: { extensions: ['.js', '.jsx', '.ts', '.tsx'], }, }, }, // ↑↑↑ADD↑↑↑ }
env:
の jest: true
がないと、テストに対して 'test' is not defined no-undef
と怒られます。入れておきましょう。
import
文にて 'React' was used before it was defined no-use-before-define
と怒られることがあるので、 rules:
の先頭2行に no-use-before-define
に関する設定を入れています。なんだかバージョンによってはうまく動くよってな意見もありましたので、環境によっては不要かもしれません。
他にも、 rules:
と settings:
については追記した量が多いのですべての説明は省きます。
ESLint のコマンド実行
TypeScript のファイルを対象に ESLint を試しに実行してみます。コマンドは npx eslint . --ext .tsx --ext .ts
です。
$ npx eslint . --ext .tsx --ext .ts (略)\src\App.tsx 11:16 error `code` must be placed on a new line react/jsx-one-expression-per-line 11:40 error ` and save to reload. ` must be placed on a new line react/jsx-one-expression-per-line ...(中略)... ✖ 6 problems (6 errors, 0 warnings) 6 errors and 0 warnings potentially fixable with the `--fix` option.
6つのエラーが出ました。「fixable with the --fix
option.」とある通り --fix
オプションを付けると強制修正を行ってくれます。この後 Prettier を入れますので、いま時点ではそのままにしておきます。
一点、「code
must be placed on a new line」の指摘だけ直しちゃいます。
// ./src/App.tsx // 修正前 <p> Edit <code>src/App.tsx</code> and save to reload. </p> // 修正後 <p>Edit >src/App.tsx and save to reload.</p>
どのみち削除してしまうコードですので放ったらかしでも良いんですが、 ESLint がうるさいので <code>
を削ってしまいます。まあ別に今やらなくてもいいとは思ってますが。
Prettier の導入と設定
続いて Prettier を導入し、設定を行いましょう。
Prettier の導入
Prettier および ESLint と連携するためのライブラリを導入します。
$ npm install ---save-dev prettier eslint-config-prettier eslint-plugin-prettier
ESLint の Prettier に関する設定
ESLint の設定を見直します。
// .eslintrc.js module.exports = { env: { browser: true, es2021: true, jest: true, }, extends: [ 'plugin:react/recommended', 'airbnb', 'plugin:prettier/recommended', // <=ADD 'prettier/@typescript-eslint', // <=ADD ], parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 12, sourceType: 'module', }, plugins: [ 'react', '@typescript-eslint', 'prettier', // <=ADD ], rules: { 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': ['error'], '@typescript-eslint/no-unused-vars': 'error', 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }], 'import/extensions': [ 'error', { extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] }, ], 'react/prop-types': 'off', 'spaced-comment': ['error', 'always', { markers: ['/ <reference'] }], 'prettier/prettier': 'error', // <=ADD }, settings: { 'import/resolver': { node: { extensions: ['.js', '.jsx', '.ts', '.tsx'], }, }, }, }
plugins:
に Prettier を、 extends:
には、 ESLint と Prettier を連動させるためのプラグインを追記しています。
rules:
に追加した 'prettier/prettier': 'error',
これなんですが、「Prettier の全ルールを一発で全部追加するぜ!」という意味です。全部まとめて error
として追加しています。(個別にレベルを設定できないという問題があるってことになりますね。これで困ったことはありませんが。)
Prettier に関する設定
さて、その ESLint と連動させた Prettier 自身の設定をしていきましょう。
まずは設定ファイルを用意します。
$ touch .prettierrc.json
設定ファイルの中身は以下のようにしておきます。ここらへんは好みでいいと思いますが、以下は、私が最小限で良さそうなものを用意したつもりです。設定内容は好みで変えてください。(セミコロンは無い方が好きな人が多そう。)
// .prettierrc.json { "endOfLine": "lf", "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" }
補足 : VSCode は再起動しておくと吉
次項では Prettier のプラグインについて書いていますが、もし既にインストール済みであったなら、エディタ上のエラーの表示がおかしくなっているのではないでしょうか? いや、おかしくなかったら良いんですが。。。
私は VSCode の再起動で表示が正されたので、取り敢えず再起動してみると良いかもしれません。いや、おかしくなかったら良いんですが。。。
ESLint のコマンド実行(Prettier 付き)
Prettier を連動させた状態で試しに実行してみましょう。
$ npx eslint . --ext .tsx --ext .ts (略)\src\react-app-env.d.ts 1:40 error Delete `␍` prettier/prettier ✖ 1 problem (1 error, 0 warnings) 1 error and 0 warnings potentially fixable with the `--fix` option.
なんか改行コードがおかしいっぽいですね。
ファイルを開いて直接修正してもよいですが、試しに強制修正を実施してみましょう。
$ npx eslint . --ext .tsx --ext .ts --fix
これでエラーが消えました。
VSCode の設定
プラグインの導入
毎回 npx eslint ...
コマンドを打つのもいいですが、自動でチェックしてコード上に表示してくれると嬉しいです。そのために ESLint と Prettier のプラグインを導入します。
プラグインの導入方法は下記に書きましたので割愛します。(プラグイン入れるだけなので大した話ではないですが。。。)
プラグインの利用を促す
VSCode ではプロジェクトにて推奨するプラグインの利用を促すことができます。 .vscode/extensions.json
を書いておけばVSCodeでプロジェクトを開いた時に推奨プラグインとして出せます。
まずはファイルを用意します。
$ mkdir .vscode $ touch .vscode/extensions.json
次に、利用を促すプラグインを記載します。
// .vscode/extensions.json { "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] }
もし、プラグインが入っていない場合は VSCode から以下のような催促が入ります。
インストールボタンを押せば、2つのプラグインがインストールされます。
ファイル保存時に強制的に --fix をかける
npx eslint (...略...) --fix
で強制修正ができました。この修正をファイル保存時に実行されるよう設定します。
まずは設定ファイルを用意します。
$ touch .vscode/settings.json
次に、ファイル保存時に設定する内容を記載します。(他の設定も混じってます。)
// .vscode/settings.json { "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "eslint.format.enable": true, "files.eol": "\n" }
上記はファイルを touch
で作成しましたが、以下の過去記事では、
次に VSCode の設定を行います。
Ctrl + ,
で設定を開き、 Workspace タブにして、code action on save
で検索します。GUIでそのまま設定したいところですが、ここでは「Edit in setting.json」をクリックします。すると.vscode/setting.json
が生成され、このファイルが開きます。
という説明をしました。どちらも結果は同じになりますので、お好きな方法で設定ファイルを用意してください。
ESLint をファイル保存で実行
それでは VSCode に設定した内容が反映されたか試してみましょう。
何でもいいのですが、取り敢えず App.tsx
の1行目のセミコロンを外して、それから保存をしてみます。
// ./src/App.tsx // 試しに修正 import React from 'react' // 最後のセミコロンを外す // Ctrl + s で保存する import React from 'react'; // 自動でセミコロンが付与された
セミコロンが自動で付与されたら成功です。