Reactでかっこいいページを作りたい!
- 国内ではVue.jsの人気も高いですが、Reactの方が海外では流行っている
-
React Server Components
なるものが出てきた - 下火気味?だけどReact Nativeをもしやる事になっても、少しは対応しやすくなるかも
ということで、
バックエンドが専門の私ですが、JavaScriptのスキルアップかねてReactをはじめました。
React & Material UI & Django RestFramework記事一覧
随時、ルーティング、ReactでAPI取得、バックエンドでAPIサーバー作ってReactで取得する方法をシリーズで書いていきます😄
内容 | |
---|---|
part1 | React Material UIを使ってみる ← ココ |
part2 | 【React】#2 React axiosでAPI データ取得 |
part3 | 【React】 #3 ルーティング・ページ遷移 react-router-dom |
part4 | 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ |
デザインはMaterial UIに任せよう
デザイナーでもフロントエンジニアでもない私は、CSS Frameworkに頼ります!
情報量の多さから、Material UIを使うのが良いと判断して勉強開始。
ReactもMaterial UIも英語読めないと対処しにくいところあるので、
誰かの助けになればいいなと思って作りながらメモしながら書いてます。
使用環境
- react 17.0.1
- material-ui 4.11.2
トピック
対象:
- react初心者
- CSS苦手な人へ
- Reactやってみたいけど、デザインが…
- とりあえず見た目が及第点欲しい人の基礎の基礎
やること
- インストール、
react-create-app
から始める - ヘッダー作成
- グリッド(レスポンシブ)
- Reactでのアイコン付け方
- カード、ボタン、画像を使って見た目を整える
環境作る
前提:nodejs入れてない人はインストールしてください。nodejsと調べて、LTSという方をダウンロードしてインストール進めればOKなはずです。
Reactのファイル作りたいところに移動してから、以下でアプリ作ります。
名前はmaterial-react
で作ってみます。
$ npx create-react-app material-react
$ cd material-react
サーバーを起動。
$ npm start
reactのマークがくるくる回ってたらOK
Material-UI使えるようにする
公式サイトの通りにインストールしていく。
$ npm install @material-ui/core
必須ではないけど、以下をやっていきます。
index.htmlにフォントを追記。titleタグ付近にでも。
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
'Roboto'つかうと書いてあるので、フォントの優先度をあげます。
body {
margin: 0;
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
アイコンを使うので、インストール。
$ npm install @material-ui/icons
デフォルトのページいらないので変更
軽く説明すると、
- index.htmlの
<div id="root"></div>
この要素をJSでいじりまくってタグとか生成して作っていく感じ - App.jsがJSファイルの親玉?先祖?みたいな感じ
- componentsフォルダ作って、その中に部品となるもの作り、App.jsにコンポーネントを埋め込んで作っていくが一般的。例えば、サイドバー用のJS、ヘッダー用のJSとか。
では、デフォルトページを修正。
初期状態では、コンポーネントが存在しないで、App.jsに全てが書いてあります。
全部をApp.jsに作ることもできますが、
Reactは機能ごとにコンポーネントに分けて作るのが普通です。
では、Reactマークのくるくるさせるための記述と、
デザインが、App.jsとApp.cssに書かれていますので、
Material-UI使うために、白紙のページにします。
白紙ページにする
- App.css中身全部消す
- App.jsの項目削除して真っ白なページにする
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
真っ白
</div>
);
}
export default App;
Reactマークが消えて、真っ白とだけ表示されてればOK
グリッド、レスポンシブ
Bootstrapと同じですね。
画面を横方向を12分割にしてその要素は、12個ののうち、何個分の横幅を使うの?
ってやつです。
flexbox使うより簡単にレスポンシブ対応できるので、専門でない人には便利です!
https://material-ui.com/components/grid/#grid
※いまいちわからない方は↑ページ開いて、ブラウザのウィンドウを小さくしたり、大きくしたりしてみてください。
import './App.css';
import { Grid } from '@material-ui/core';
function App() {
return (
<Grid container direction="column">
<Grid item>
item1item1item1item1item1item1item1item1item1item1item1item1
</Grid>
<Grid item container>
<Grid sm={2} />
<Grid xs={12} sm={8}>
item2item2item2item2item2item2item2item2item2item2item2item2
item2item2item2item2item2item2item2item2item2item2item2item2
item2item2item2item2item2item2item2item2item2item2item2item2
item2item2item2item2item2item2item2item2item2item2item2item2
item2item2item2item2item2item2item2item2item2item2item2item2
</Grid>
<Grid sm={2} />
</Grid>
</Grid>
);
}
export default App;
<Grid container>
という親の中に<Grid item>
という子を入れていきます。
<Grid item container>
分割された子の要素の中をさらに分割するため、container入れてます。
<Grid sm={2} />
これが二つありますが、これが左右の余白です。
xsが超小さい画面の時には余白なし。小さい画面以上の時は、 2/12の空白を入れてます。
2/12が二つなので、本体は残りの8/12。
なので<Grid xs={8}>
ブラウザの画面を手動で大きくしたり、小さくしたりして変化するか試してください。
ヘッダーを作る
srcのなかにコンポーネントフォルダを作ります。
その中にHeader.js
作成
$ mkdir src/components
import React from 'react'
function Header() {
return (
<div>
ヘッダー
</div>
)
}
export default Header
ヘッダーコンポーネントをApp.jsに追加
作ったヘッダーコンポーネントを親玉のApp.jsの中につっこみます。
import Header from './components/Header';
・・・
<Grid container direction="column">
<Grid item>
<Header />
</Grid>
...
単純に**「ヘッダー」**という文字が出るだけですが、
ヘッダーコンポーネント(ヘッダーの部品)が読み込まれてます。
これだとしょぼいので、公式サイトから使用例をパクって改造します。
https://material-ui.com/components/app-bar/
https://material-ui.com/ja/api/app-bar/
基本的なものだけで構成してみました。
import { AppBar, Toolbar, Typography } from '@material-ui/core'
import React from 'react'
function Header() {
return (
<AppBar position="static">
<Toolbar>
<Typography>ヘッダー</Typography>
</Toolbar>
</AppBar>
)
}
export default Header
ヘッダーなのでposition="static"
にしてます。
これ指定しないと、ヘッダーに要素が重なってしまいます。
アイコンをつける
検索して良いアイコンを探す。
好きなアイコンをクリックすると下の画像みたいな表示が出る。
今回はこのシルエットみたいなアイコンを試しに貼り付けてみます。
importのところをコピーしてHeader.jsに貼り付け。iconを使えるようにします。
import AccountCircleOutlinedIcon from '@material-ui/icons/AccountCircleOutlined';
<AppBar position="static">
<Toolbar>
<Typography>ヘッダー</Typography>
<AccountCircleOutlinedIcon />
</Toolbar>
</AppBar>
ただ、普通、こういうのって、左端にありますよね。
ということで、CSSをいじって、調整します。
Material-UIでCSSを追加
makeStyleを使ってみましょう。
Material-UIで作るときにCSSを書くとき使うもののようです。
ファイル一つにたくさんのCSS書くより、コンポーネントに分かれてるし、そこにCSS書けばよくない?
ってことで使われるらしいです。(?)
当然、JSで書くので計算とか、条件とか、Scssっぽく?便利にデザインを適用もできますね。
import React from "react";
import { AppBar, Toolbar, makeStyles, Typography } from "@material-ui/core";
import AcUnitRoundedIcon from "@material-ui/icons/AcUnitRounded";
const useStyles = makeStyles(() => ({
typographyStyles: {
flex: 1
}
}));
const Header = () => {
const classes = useStyles();
return (
<AppBar position="static">
<Toolbar>
<Typography className={classes.typographyStyles}>
Anthony sistilli
</Typography>
<AcUnitRoundedIcon />
</Toolbar>
</AppBar>
);
};
export default Header;
className
はHTMLのclassのことです。reactの中ではclassNameと書きます。
で、クラスをmakeStyleで生成するということで↓追加。
const useStyles = makeStyles(() => ({
typographyStyles: {
flex: 1
}
}));
useStyles作って
const classes = useStyles();
でclasses作って、
classNameにオブジェクトのキー名typographyStyles
を設定しているので
classes.typographyStyles
で、ここの要素にflex: 1
を適用するということです。
コンテンツ部分のコンポーネントを作る
$ touch src/components/Content.js
Content.js
作成。App.js
に
ヘッダーの時と同じようにインポートと、<Content />
とを入れる。
import Content from './components/Content';
...
<Grid container direction="column">
<Grid item>
<Header />
</Grid>
<Grid item container>
<Grid sm={2} />
<Grid xs={12} sm={8}>
<Content />
</Grid>
<Grid sm={2} />
</Grid>
</Grid>
コンテンツ部分は
「カード」
を使います。
カードの部分もコンポーネントにしたいのでBodyCard.js
を作ります。
$ touch src/components/BodyCard.js
Contentコンポーネントの中にBodyCardコンポーネントが入れ子になってます。
App.jsからみるとBodyCardコンポーネントは孫に当たります。
では、Outlined Cardというのを公式ページからパクってきて、
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
const useStyles = makeStyles({
bullet: {
display: 'inline-block',
margin: '0 2px',
transform: 'scale(0.8)',
},
title: {
fontSize: 14,
},
pos: {
marginBottom: 12,
},
});
function BodyCard() {
const classes = useStyles();
const bull = <span className={classes.bullet}>•</span>;
return (
<Card variant="outlined">
<CardContent>
<Typography className={classes.title} color="textSecondary" gutterBottom>
Word of the Day
</Typography>
<Typography variant="h5" component="h2">
be{bull}nev{bull}o{bull}lent
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
);
}
export default BodyCard
カードを一つだけ表示させてみます。
import React from 'react'
import BodyCard from './BodyCard'
function Content() {
return (
<BodyCard />
)
}
export default Content
3個表示すると
import { Grid } from '@material-ui/core'
import React from 'react'
import BodyCard from './BodyCard'
function Content() {
return (
<Grid container>
<Grid item xs={4}>
<BodyCard />
</Grid>
<Grid item xs={4}>
<BodyCard />
</Grid>
<Grid item xs={4}>
<BodyCard />
</Grid>
</Grid>
)
}
export default Content
スペースを入れて見た目調整
くっついていて見た目良くないので、調整します。
Material-UI公式ページのgridの項目で
spacingの項目で確認してからやるといいかもしれません。
spacing={2}
足す。もっと開けたいときは数字を大きくするといいです。
return (
<Grid container spacing={2}>
<Grid item xs={4}>
<BodyCard />
</Grid>
<Grid item xs={4}>
<BodyCard />
</Grid>
<Grid item xs={4}>
<BodyCard />
</Grid>
</Grid>
)
あとは、好みで調整してみてください。
画面のサイズによってどのような横幅にするかをxs, sm md, lg, xl
で決められます。
Bootstrapと同じ感じ。
今回は、スマホなど画面が非常に小さくなると、カードが一つずつ。smサイズで1行に3個並べるようにしました。
↓xsサイズにすると
カードの見た目改造 カードのヘッダー、クリックできるアイコン
カードの中のヘッダーとクリックできるアイコンを用意します。
今回は⭐️アイコンつけます。
Card要素の一番初めにCardHeaderを追加して、
⭐️アイコンを調べてインポート(StarBorderOutlinedIcon)。
該当箇所に貼り付けます。
import CardHeader from '@material-ui/core/CardHeader';
import Avatar from '@material-ui/core/Avatar';
import IconButton from '@material-ui/core/IconButton';
import StarBorderOutlinedIcon from '@material-ui/icons/StarBorderOutlined';
<Card variant="outlined">
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
R
</Avatar>
}
action={
<IconButton aria-label="settings">
<StarBorderOutlinedIcon />
</IconButton>
}
title="Shrimp and Chorizo Paella"
subheader="September 14, 2016"
/>
IconButtonがあるとホバーした時に違い出て、押せる感じになってます。
アバターと画像は適当な画像をランダムで取得できるサービスあったのでテストで使います。
import { CardMedia } from '@material-ui/core';
.
.
.
function BodyCard(props) {
const { avatarUrl, title, subheader, text, imageUrl } = props;
const classes = useStyles();
const bull = <span className={classes.bullet}>•</span>;
return (
<Card variant="outlined">
<CardHeader
avatar={<Avatar src={avatarUrl} />}
action={
<IconButton aria-label="settings">
<StarBorderOutlinedIcon />
</IconButton>
}
title={title}
subheader={subheader}
/>
<CardMedia style={{ height: "150px" }} image={imageUrl} />
<CardContent>
<Typography variant="body2" component="p">
{text}
</Typography>
</CardContent>
<CardActions>
<Button size="small">詳細をみる</Button>
</CardActions>
</Card>
);
}
.
.
.
function Content() {
return (
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<BodyCard
title="タイトル1"
subheader="サブヘッダー1"
avatarUrl="https://joeschmoe.io/api/v1/random"
imageUrl="https://picsum.photos/150"
text="カードの説明1" />
</Grid>
<Grid item xs={12} sm={4}>
<BodyCard />
</Grid>
<Grid item xs={12} sm={4}>
<BodyCard />
</Grid>
<Grid item xs={12} sm={4}>
<BodyCard />
</Grid>
</Grid>
)
}
今は、propsを一個めのカードにしか与えてないので、このようになってます。
では、残りのカードにも表示されるようにして、
import { Grid } from '@material-ui/core'
import React from 'react'
import BodyCard from './BodyCard'
const cardContents = [
{
title: "タイトル1",
subheader: "サブヘッダー1",
avatarUrl: "https://joeschmoe.io/api/v1/random",
imageUrl: "https://picsum.photos/150"
},
{
title: "タイトル2",
subheader: "サブヘッダー2",
avatarUrl: "https://joeschmoe.io/api/v1/random",
imageUrl: "https://picsum.photos/150"
},
{
title: "タイトル3",
subheader: "サブヘッダー3",
avatarUrl: "https://joeschmoe.io/api/v1/random",
imageUrl: "https://picsum.photos/150"
},
{
title: "タイトル4",
subheader: "サブヘッダー4",
avatarUrl: "https://joeschmoe.io/api/v1/random",
imageUrl: "https://picsum.photos/150"
},
]
function Content() {
const getCardContent = getObj => {
return (
<Grid item xs={12} sm={4}>
<BodyCard {...getObj} />
</Grid>
);
};
return (
<Grid container spacing={2}>
{cardContents.map(contentObj => getCardContent(contentObj))}
</Grid>
)
}
export default Content
同じ画像になりますができました。
次回はデータをAPIで取得して表示する方法について触れます。
React & Material UI & Django RestFramework記事一覧
内容 | |
---|---|
part1 | React Material UIを使ってみる ← ココ |
part2 | 【React】#2 React axiosでAPI データ取得 |
part3 | 【React】 #3 ルーティング・ページ遷移 react-router-dom |
part4 | 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ |