🚀

[React] 初心者講座01 <create-react-app>

2021/11/26に公開

Recoil(Reactの状態管理)に興味があるのでReactのTemplateが欲しくなった。
せっかくなのでTemplate作成手順をまとめる。

ReduxとかMiddlewareとかはふれない。
Recoilもまだ、さわれていない。

目標

  • create-react-appができる
  • router下準備ができる

完成形

2021/11月現在、ヘッダしかない。

ソースは以下。
https://github.com/kosukekashiwa/cra-sample

nodeは以下のverにアップデートしてから実装。

$ node -v
v16.13.0
$ npm -v
8.1.0
$ npx create-react-app --version
4.0.3

create-react-app

まずはcreate-react-app
typescriptで作成する。cra-sample部分は好きな名前で良い。
craはcreate-react-appの頭文字をとった。

$ npx create-react-app cra-sample --template typescript

VSCode起動時にtsconfig.jsonApp.tsxに以下のエラーが出た。

  • --jsx' オプションの引数は 'preserve', 'react-native', 'react' である必要があります。
  • --jsx' フラグが指定されていないと、JSX を使用できません。

解決方法は、tsxファイルを選択しているときVScodeの右下にTypeScriptのverが表示されている。
クリックでver変更でき、3.8.34.4.4にしたら直った。

起動してみる。http://localhost:3000/で見れる。

$ npm start

Prettier and ESLint

とりあえずlinterを入れておく。

$ ./node_modules/.bin/eslint --init
✔ How would you like to use ESLint? -> To check syntax, find problems
✔ What type of modules does your project use? -> JavaScript modules (import/export)
✔ Which framework does your project use? -> React
✔ Does your project use TypeScript? -> Yes
✔ Where does your code run? · Browser
✔ What format do you want your config file to be in? -> JavaScript
✔ Would you like to install them now with npm? -> Yes
$ npm install --save-dev eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin
$ npm install --save-dev prettier eslint-plugin-prettier eslint-config-prettier
$ echo {}> .prettierrc.json
$ echo > .prettierignore
.prettierrc.json
{
  "printWidth": 100,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "all"
}
.prettierignore
build
coverage

サブディレクトリ

src直下に以下の構成でサブディレクトリを作成する。
ついでにApp.tsxviews直下に移動しておく。
さらについでにtestファイルは今は不要なので消す。

  • src/
    • views/
      • atoms/
      • organisms/
      • ecosystems/
      • environments/

https://zenn.dev/kosukek/articles/5972bcb65c4d3d

MUI(Material-UI)

デザインシステムとしてMUI(Material-UI)を入れる。
MUI v5よりemotion or styled-componentsが必須になった。
[参考]npm trands:@emotion/styled vs styled-components

$ npm install @mui/material @emotion/react @emotion/styled

アイコンはMaterial icons。SVGなので伸縮してもキレイ。

$ npm install @mui/icons-material

package.jsonメモ
"@mui/material": "^5.1.1"
""@emotion/react": "^11.6.0"
@emotion/styled": "^11.6.0"

試しにButtonを表示する。使い方は公式を見る。
App.tsxを少し書き換えた。<Box>タグはMUIにおける便利な<div>タグ。
Componentの書き方の解説はもう少し後にする。

views/App.tsx
import React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

const App: React.VFC = () => {
  return (
    <Box>
      <Box>Create React App Sample</Box>
      <Button variant="contained">Test!</Button>
    </Box>
  );
};

export default App;

MUIのstyleを変えれるようにThemingを導入する。
createTheme内でpaletteをいじるとベースカラーを変更できる。

views/theme.ts
import { createTheme } from '@mui/material/styles';
import { blueGrey, green } from '@mui/material/colors';

const theme = createTheme({
  palette: {
    primary: {
      main: green[500],
    },
  },
  components: {
    MuiCssBaseline: {
      styleOverrides: {
        body: {
          backgroundColor: blueGrey[50],
        },
      },
    },
  },
});

export default theme;

theme.tsはviews直下に配置しておいて、index.tsにimportする。
ついでにCssBaselineも入れる。

index.tsx
import { CssBaseline, ThemeProvider } from '@mui/material';
import theme from './views/theme';
...

ReactDOM.render(
  <React.StrictMode>
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <App />
    </ThemeProvider>
  </React.StrictMode>,
  document.getElementById('root'),
);

ところでMUIのButtonはデフォルトのstyleでtext-transform: uppercase;が設定されているため、英文字はすべて大文字になる。
不要だったら以下のように書き換えてしまってもよい。

views/theme.ts
const theme = createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          textTransform: 'none',
        },
      },
    },
  },
});

paletteについては公式のカラーパレットを使ってもいいし、カラーパレットのサイトを参考にしても良い。
以下、抜粋。
https://saruwakakun.com/design/gallery/palette
http://mcg.mbitson.com/#!?mcgpalette0=%233f51b5

ルーティング

$ npm install react-router-dom@5
$ npm install --save-dev @types/react-router-dom

App.tsxで表示させたいComponentをBrowserRouter Switch Routeでラップする。
以下の例では起動時のパスが/なのでcra-appにRedirectし<Home />を表示する。

exact strict sensitive等はpathをどれだけ正確に判定するかの属性でこちらで詳細を確認できる。

App.tsx
import React from 'react';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import Home from './environments/Home';

const App: React.VFC = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Redirect exact strict from="/" to="/cra-app" />
        <Route sensitive path="/cra-app">
          <Home />
        </Route>
      </Switch>
    </BrowserRouter>
  );
};

<Home />のルーティングの仕組みをざっくり作ってしまう。
以下は<App />からRedirectで<Home />に遷移したあと、またRedirectで<DashboardView />に遷移している。
<DataListView />にもpathを与える。

views/environments/Home.tsx
const Home: React.VFC = () => {
  const { path } = useRouteMatch();

  return (
    <Box>
      <Box>Create React App Sample</Box>

      <Switch>
        <Route exact sensitive path={`${path}`}>
          <Redirect to={`${path}/dashboard`} />
        </Route>

        <Route exact sensitive path={`${path}/dashboard`}>
          <DashboardView />
        </Route>

        <Route exact sensitive path={`${path}/data-list`}>
          <DataListView />
        </Route>
      </Switch>
    </Box>
  );
};
views/environments/dashboard/DashboardView.tsx
import React from 'react';
import Box from '@mui/material/Box';

const DashboardView: React.VFC = () => {
  return <Box>DashboardView</Box>;
};

export default DashboardView;
views/environments/hogeData/DataListView.tsx
import React from 'react';
import Box from '@mui/material/Box';

const DataListView: React.VFC = () => {
  return <Box>DataListView</Box>;
};

export default DataListView;

ボタンを押すことでの遷移処理は未実装だが、urlの末尾を

  • http://localhost:3000/cra-app/dashboard
  • http://localhost:3000/cra-app/data-list

のように直接書き換えると画面の表示が切り替わっていることを確認できる。

このとき、Create React App Sampleは常に表示されていることが分かる。
ここをに共通ヘッダComponentを定義すれば常に表示されることになる。

遷移処理はもう少し後に記述する。

基本の画面レイアウトを実装

画面レイアウトは以下のようにする。
scrollに応じてヘッダが隠れたりするものが流行っている気もするが今回は固定。

上図の黒枠部分のComponentをatomsに実装。
Componentの基本的な書き方はこれらの後に説明する。

views/atoms/containers/AppContainer.tsx
import React from 'react';
import Box from '@mui/material/Box';

type AppContainerProps = {
  children: React.ReactNode;
};

const AppContainer: React.VFC<AppContainerProps> = (props) => {
  return (
    <Box display="flex" flexDirection="column" height="100vh">
      {props.children}
    </Box>
  );
};

export default AppContainer;

同様に赤枠部分のcomponentの実装。

views/atoms/containers/MainContainer.tsx
const MainContainer: React.VFC<MainContainerProps> = (props) => {
  return (
    {/* marginとか余白はまた今度 */}
    <Box flexGrow={1} sx={{ overflowY: 'auto' }}>
      {props.children}
    </Box>
  );
};

ついでに青枠部分のヘッダComponentをorganismsに仮実装する。

ヘッダは複数作らない気がするので、サブディレクトリは作らない。増えたらまた考える。
ここでポイントとなっているのはヘッダに表示するアプリ名をpropsで受け取っていること。

organismsComponentの責務は「レイアウトを整えて表示」することである。

views/organisms/AppHeader.tsx
import React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Box from '@mui/material/Box';

type AppHeaderProps = {
  appTitle: string;
};

const AppHeader: React.VFC<AppHeaderProps> = (props) => {
  return (
    <AppBar position="static">
      <Toolbar>
        <Box>{props.appTitle}</Box>
      </Toolbar>
    </AppBar>
  );
};

export default AppHeader;

上記のComponentを組み込んだ<Home />が以下。

views/environments/Home.tsx
const Home: React.VFC = () => {
  const { path } = useRouteMatch();

  return (
    <AppContainer>
      <AppHeader appTitle="Create React App Sample" />

      <MainContainer>
        <Switch>
          <Route exact sensitive path={`${path}`}>
            <Redirect to={`${path}/dashboard`} />
          </Route>

          <Route exact sensitive path={`${path}/dashboard`}>
            <DashboardView />
          </Route>

          <Route exact sensitive path={`${path}/data-list`}>
            <DataListView />
          </Route>
        </Switch>
      </MainContainer>
    </AppContainer>
  );
};

続き

https://zenn.dev/kosukek/articles/85604d37ea1b0e

Discussion