20201104のReactに関する記事は8件です。

ReactDatepickerで週の開始日を任意の曜日にする方法

早速サンプルコード

import ja from 'date-fns/locale/ja';
import React, { ReactElement } from 'react';
import DatePicker from 'react-datepicker';

// Locale Classのインスタンスではないが、Typescriptのコンパイルエラーを潰すために強引に
// インスタンスをちゃんと作る方べきかもだが今回はasで簡単に回避
const sundayStartJa = { ...ja, options: { ...ja.options, weekStartsOn: 0 } } as Locale; 

export const Datepicker = (): ReactElement => 
  <DatePicker locale={sundayStartJa} onChange={console.log} />;

上記の前提は

  • ロケールはja (日本)

    • jaにすると、週の開始日が月曜日になる
  • これを日曜日開始に変更する

    • weekStartsOn: 0が日曜なので任意の曜日を指定する

ドキュメントにも見当たらず、だいぶ無理やり実装した感があるが想定どおりの挙動をしている
ja.option = {//略}などでも動いたが、怖いのでコピーを作成

React Datepicker
https://reactdatepicker.com/
https://github.com/Hacker0x01/react-datepicker#localization

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.js × TypeScript × Material-UI on docker環境を作ってみた

本記事について

本記事は、Docker上でReact×TypeScript×Material-uiが動く環境を作ってみた記事になります。
これからDockerで開発環境をサクッと作りたい方々にとっては参考になる部分もあるかなと思います!
もし、内容等に間違い、またアドバイス等ございましたらご教示いただけると非常に助かります。。

今回使用した主な技術は以下になります。

  • Docker,Docker-compose
  • React.js (version.17.0.1)
  • TypeScript
  • material-ui (version.4.11.0)

作成前のスペック

  • VueでSPAを作ったことはある。
  • Dockerはなんとなく・・(実務未経験)

作ろうと思った背景

最近Vueが少しわかるようになってきて、他のJSのFWやライブラリについても勉強して、
新たな知見が得られればいいなぁ。。と思い、ReactとTypeScriptを勉強し始めました。

とりあえず理解を深めるために、なにか形になるものを作りたいなー。と考え、
まずは開発環境を構築しよう!ということで、今回やってみました。

開発環境作成

ひとまず、作業用ディレクトリを切ります。
今回は、react-todos(ディレクトリ名)を切りました。

mkdir react-todos

次に、プロジェクトのルートディレクトリに、Dockerfile、docker-compose.ymlをそれぞれ作成します。

Dockerfile作成

#Dockerfileの作成
vi Dockerfile

ベースとなるimageの取得。今回は最新版(2020/11/1時点で)を取ってきました。
https://hub.docker.com/_/node

Dockerfile
FROM node:15.0.1-alpine3.10

docker-compose.ymlの作成

https://docs.docker.com/compose/compose-file/ からテンプレートを取得し、ゴニョゴニョします。

#docker-compose.ymlの作成
vi Docker-compose.yml
docker-compose.yml
version: "3.8"
services:
  front:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./front:/front
    working_dir: /front
    command: node
    tty: true
# コンテナ立ち上げ
docker-compose up

nodeのコンテナが立ち上がったことが確認できたら、コンテナ内部に入り、Reactのプロジェクトを作成していきます。

コンテナ内部に入り、Reactプロジェクト作成。

# コンテナに入る。
docker-compose exec front sh
# Create React Appで、TypeScriptを使用するには、以下のコマンドを実行します。
npx create-react-app my-app --template typescript

front/my-app配下に、Reactのプロジェクトが作成されたことを確認できたら、
再度Dockerfileと、docker-compose.ymlファイルを修正し、コンテナを立ち上げます!

Dockerfile
FROM node:15.0.1-alpine3.10
#下2行を追記!
COPY ./front /front 
WORKDIR /front 
docker-compose.yml
version: "3.8"
services:
  front:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./front:/front
    working_dir: /front/my-app #修正!
    command: sh -c "npm install & npm start" #修正!
    tty: true
    ports:
      - 3000:3000 #ポートを指定!

再度コンテナを立ち上げて、http://localhost:3000/ にアクセスできるか確認しましょう!

# コンテナ立ち上げ
docker-compose up

無事に画面が起動したでしょうか??

Material-uiのインストール

次に、ReactのUIフレームワークである、Material-uiをインストールしていきます。再びコンテナの内部に入ります。

# /front/my-appディレクトリ内でインストール
npm install @material-ui/core
npm install @material-ui/style

しかし、、ここで問題が発生。。インストールができない。。
どうやら、Reactのバージョンが、material-uiのバージョンと合わなかったため、依存関係のエラーが出ていたよう・・
詳細はこちらで記述しました。

https://qiita.com/koh97222/items/c46d1ef2a63b92bb6c15
(ERESOLVE unable to resolve dependency treeの解決方法)

結果、https://github.com/mui-org/material-ui/issues/23306 を参考に、以下のコマンドを実行。

# /front/my-appディレクトリ内でインストール
npm install --save --legacy-peer-deps @material-ui/core

installできたら、tsconfig.jsonを修正し、TypeScript環境でもmaterial-uiが動くようにします。

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["ES6", "dom", "dom.iterable", "esnext"], // ES6を追加
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    // 3行を追加
    "noImplicitAny": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "jsx": "react"
  },
  "include": ["src"]
}

tsconfig.jsonが修正できたら、material-uiが実際に使えるかどうか試すために、一個コンポーネントを作ってみようと思います。
今回は、srcディレクトリの配下にcomponentsディレクトリを作成し、
そのディレクトリ内で、ButtonコンポーネントをラップしたWrapButton.tsxを作成しました。

.components/WrapButton.tsx
import React from "react";
import { Button } from "@material-ui/core";

const WrapButton = () => {
  return (
    <>
      <Button variant="contained" color="secondary">
        Hello World!
      </Button>
    </>
  );
};

export default WrapButton;
App.tsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import WrapButton from "./components/WrapButton";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
     // ↓追加
        <WrapButton />
      </header>
    </div>
  );
}

export default App;

スクリーンショット 2020-11-02 23.56.07.png

ちょっと不格好ですが、Buttonコンポーネントが表示できていることが確認できましたね!(メールの通知は気にしないでください・・笑)
ひとまず開発環境が整ったので、ここから好きなようにゴニョゴニョしていきましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを学ぶV(コンポーネント間でデータの受け渡しをする)

■ はじめに

タイトルについて記事にしました。
この記事で得る内容は以下の通りです。

・コンポーネントの概要
・コンポーネントを使う理由
・コンポーネントの種類
・propsでデータを受け渡すには
・受け渡せるデータ型

■ コンポーネントとは(復習)

UIは2つに分類される

1.見た目(View)
2.機能(Controller)

コンポーネントとは、見た目と機能を合わせ持ったもの

■ なぜコンポーネントを使うのか

・ 再利用するため

→コンポーネントにprops(引数みたいなもの)を渡す事で、例えばログイン・ログアウトボタンを作る時にボタンだけコンポーネントを作っておき、文字だけを変えて再利用するなど

・ 分割統治するため

→コンポーネントを分けておくことにより、管理がしやすくなる

・ 変更に強くするため

→ コンポーネントが分かれていることにより、他に影響を与えず変更に対応できる

■ コンポーネントの種類

Class Component

・ クラスによって管理されたコンポーネント
・ React.Componentを継承
・ ライフサイクルやstateを持つ
・ propsにthisが必要
・ renderメソッド内でJSXをreturnする

src/Article.jsx
import React from 'react';

class Article extends React.Compornent {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div>
                <h2>{this.props.title}</h2>
            </div>
        );
    }
}

Functional Component

・ 関数型で管理されたコンポーネント
・ ES6のアロー関数で記述
・ stateを持たない(stateless)
・ JSXをreturnする

src/Article.jsx
import React from 'react';

const Article = (props) => {
    return (
        <div>
          <h2>{props.title}</h2>
        </div>
    );
};

・ Class Componentよりも、Functional Componentがよく使われている
→ 記述がシンプルなのもあるが、なるべくstateを持たせたくないという考え方のため

■ 例

例えば、Blog.jsxという親コンポーネントと、Article.jsxという子コンポーネントがあったとして

Blog.jsx(親コンポーネント)
import React from "react";
import Article from "./Article";

class Blog extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <>
        <Article title="react" />
      </>
    );
  }
}

export default Blog;
Article.jsx(子コンポーネント)
import React from "react";

const Article = (props) => {
  return (
    <div>
      <h2>{props.title}</h2>
    </div>
  );
};

export default Article;

親コンポーネントから引数を渡して、子コンポーネントはpropsという形で受け取って

react.png

呼び出して、こんな感じで表示できる

■ 受け渡せるデータ型

Blog.jsx
import React from 'react';
import Article from "./Article";

const authorName = "s79ns";
const Blog = () => {  // {}内に記述
    return (
        <div>
            <Article title="React"  // 文字列{}無しでも可
            order={3} // 数値
            isPublished={true}  // 真偽値
            author={authorName}  // 変数など何でも渡せる
            />
        </div>
    );
};
export default Blog;

■ 再利用する

3つArticle titleにそれぞれ異なる文字列を入れてブラウザの画面を見ると

Blog.jsx
import React from "react";
import Article from "./Article";

class Blog extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <>
        <Article title="reactとは" />
        <Article title="環境構築" />
        <Article title="実際に使ってみよう" />
      </>
    );
  }
}

export default Blog;

それぞれ異なる値で渡されて表示される

react再利用.png

更に、一例ですが記事タイトルの下に順番を表示したい場合は、Article titleの横にorderと定義して

Blog.jsx
import React from "react";
import Article from "./Article";

class Blog extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <>
        <Article title={"reactとは"} order={1} />
        <Article title={"環境構築"} order={2} />
        <Article title={"実際に使ってみよう"} order={3} />
      </>
    );
  }
}

export default Blog;

{props.title}の下に、文字列{props.order}と呼び出してあげることで

Article.jsx
import React from "react";

const Article = (props) => {
  return (
    <div>
      <h2>{props.title}</h2>
      <p>順番は{props.order}です</p>
    </div>
  );
};

export default Article;

react再利用order.png

この様に表示することができ、繰り返し書かず、変更に強いという特徴が分かる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase+Reactで会員登録機能を作る際に、ユーザー名(username)も一緒に登録したい!

Firebase Auth reactfirebase reactで検索すると、基本的なemail+passwordでユーザー登録する機能の作り方は出くるのですが、ユーザー名も一緒に登録する方法を調べていても一向にしっくりくる情報が得られないんですよね。。

そこで、この記事ではfirebase+reactでユーザー登録の機能を作る際に、ユーザー名もセットで登録する方法について解説します。

実装の全体像

まずは全体像を掴みましょう。

firebaseでユーザー登録といえば、Authenticationですよね。以下の画像のように、様々な認証方法があってとても便利な機能です。
スクリーンショット 2020-11-03 21.46.29.png

スクリーンショット 2020-11-03 21.49.04.png

ただ、初心者の僕はここで疑問が浮かびます。

「Authenticationでメールアドレスとパスワード、ユーザーのIdが管理できるのはわかった。だけど、ユーザー名を登録したいと思ったらどうすれば良いんだろう??」

僕はこの疑問についてずっと考えたのですが、良い答えに辿り着けず数日が経過したある日、ふと思いました。

「もしかしたら、Firestore使えばいけるんじゃね?」

そうです。firebaseでユーザー名を登録する場合はAuthenticationではなくfirestoreにデータを保存する必要があったのです。

このAuthenticationでどうすればユーザー名登録できるかみたいな考えに束縛されて、答えに辿り着けずにいました。

具体的にどうすれば良いのか

前述の通り、firestoredb.collection('users').doc(uid).set(userInitialData)を使ってusernameemailpasswordのデータを保存します。(後述します)

ユーザーを追加する方法を解説

仮に、emailpasswordだけでユーザー登録するとしたら、特にfirestoreを使う必要はなく、createUserWithEmailAndPasswordでできるのですが、ユーザー名(username)を登録するとなると、firestoreを使う必要があります。

まずは、お好みの形でFormを作成してください。

僕の場合は、Bootstrapを使っているので、SignUp.jsxファイルは以下のようになりました。
今回特にみて欲しいのはhundlesubmitの部分です。

SignUp.jsx
import React, { useRef, useState } from "react"
import { Form, Button, Card, Alert } from "react-bootstrap"
import { useAuth } from "../contexts/AuthContext"
import { Link, useHistory } from "react-router-dom"

export default function Signup() {
 // ※長くなりすぎるので、一部省略しています
  const [error, setError] = useState("")
  const [loading, setLoading] = useState(false)
  const history = useHistory()

//特にみて欲しいのはここ
  async function handleSubmit(e) {
    e.preventDefault()

    //パスワードの一致値チェック
    if (passwordRef.current.value !== passwordConfirmRef.current.value) {
      return setError("パスワードが一致しません")
    }

  //fromで送られてきた値を処理する
    try {
      setError("")
      setLoading(true)
    //signUpという別のコンポーネントに記述した関数に引数として、fromの値を渡し、実行させる
      await signup( usernameRef.current.value, emailRef.current.value, passwordRef.current.value)
      history.push("/")
    } catch {
      setError("アカウントの作成に失敗しました")
    }

    setLoading(false)
  }

  return (
    <>
      <Card>
        <Card.Body>
          <h2 className="text-center mb-4">サインアップ</h2>
          {error && <Alert variant="danger">{error}</Alert>}
          <Form onSubmit={handleSubmit}>
            <Form.Group id="username">
              <Form.Label>名前</Form.Label>
              <Form.Control type="text" ref={usernameRef} required />
            </Form.Group>
            <Form.Group id="email">
              <Form.Label>Email</Form.Label>
              <Form.Control type="email" ref={emailRef} required />
            </Form.Group>
            <Form.Group id="password">
              <Form.Label>パスワード</Form.Label>
              <Form.Control type="password" ref={passwordRef} required />
            </Form.Group>
            <Form.Group id="password-confirm">
              <Form.Label>パスワードの確認</Form.Label>
              <Form.Control type="password" ref={passwordConfirmRef} required />
            </Form.Group>
            <Button disabled={loading} className="w-100" type="submit">
              登録する
            </Button>
          </Form>
        </Card.Body>
      </Card>
    </>
  )
}

以下のファイルにSignUpの挙動を示す関数を記述します。

AuthContext.jsx
//省略しています  

 //formが値を送信するとこのSignUp関数が実行される
  function signup(username, email, password) {
    //createUserWithEmailAndPassword(これはfirebaseのメソッド)でユーザーを作成
    return auth.createUserWithEmailAndPassword(email, password)
    //ユーザーを作成したら
      .then(result => {
     //userが戻り値として返ってくるので、それをuserに代入
        const user = result.user
     //userに値が入っていれば
        if(user) {
          //userの中にあるuidをuidに代入
          const uid = user.uid
          //他にも持っている値でfirestoreに登録したいものをuserInitialDataに代入
          const userInitialData = {
            email: email,
            uid: uid,
            username: username
          }

       //firestoreのusersというコレクションに、uidをドキュメントIDとしてもつ、 userInitialDataを登録する
       firebase.firestore().collection('users').doc(uid).set(userInitialData)
            .then('ユーザーが作成されました!')
        }
      })
  }

//以下省略

auth.createUserWithEmailAndPassword(email, password)のauthはfirebaseの初期設定ファイルで、app.auth()を定数化しています。

firebase.jsx
const app = firebase.initializeApp({
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID
})

export const auth = app.auth()
export default app

ちなみに、appの部分は直接このファイルに書き込むのではなく、別に用意した.envファイルに記述して、.gitignoreしています。

その方法はこちらの記事で説明しています。

AuthContext.jsxで使っているcreateUserWithEmailAndPasswordメソッドは戻り値がPromise < UserCredential >となっています。UserCredentialはemailやuidなど、他にも様々なデータを格納しています。

今回は、そこからuserの中に入っているemailusernameuidを取り出しました。

(createUserWithEmailAndPasswordはfirebaseの公式のリファレンス(英語)にメソッドの返り値や引数などの詳細が載っています。)

そして、firebase.firestore().collection('users').doc(uid).set(userInitialData)でコレクションにデータを保存します。
繰り返しになりますが、usersというコレクションの、uidをIDにもつドキュメントに、先ほど取得したuserInitialData.set()メソッドで登録します。

これで完了です??

参考

Firebase API Reference

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】Material-UI 導入 〜 運用手順

Material-UIインストール

ターミナルの開発プロジェクト下で Material-UI の実装に必要なパッケージをインストールします

npm install --save @material-ui/core @material-ui/icons @material-ui/system

Material-UIインポート

Material-UI を使用したいファイル内で次をインポートします

import { makeStyles, createStyles } from '@material-ui/core/styles';

関数を定義

import下に次の関数を定義する

const useStyles = makeStyles(() =>
    createStyles({
        // ここでスタイルをつける
    }),
);

スタイルをつけよう

ルールとして以下の点に注意しましょう
・クラス、Id はダブルクォーテーションで囲む
・プロパティはキャメルケースでかく

//省略

const useStyles = makeStyles(() =>
    createStyles({
        "hello": {
            color: '#AAAAAA',
            fontWeight: 'bold',
        }
    }),
);

const Hello = () => {

    const classes = useStyles(); // useStyles関数を定数に代入
    return(
        <h1 className={classes.hello}>Hello World</h1> // クラスを指定する時は定数名.クラス名
    );
}

// 省略

以上

Material-UI公式ページ
https://material-ui.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MarkdownをGitHubに上げるだけでブログを公開できる仕組みをサクッと作ってみた

世の中には便利なCMSプロダクトがたくさんありますが、ぶっちゃけエンジニアだったらWeb上のフォームでコンテンツを入力するより、ローカルでMarkdownを作ってサクッとGitHubにPushするだけでブログ公開できたほうが楽だなーと思ったので作ってみました。

できるようになること

下記のようなインデックスページとブログページをMarkdownを作るだけで生成できるようになります。

画面収録 2020-10-17 12.48.55.mov.gif

ブログ公開の手順は下記の通りでとても簡単です。

  1. Markdownでブログを書く
  2. インデックスページを自動生成する
  3. GitHubにPushして自動デプロイ

実装

それでは実装していきます。

主要パッケージのバージョン

この記事を書いている時点の主要パッケージのバージョンは下記の通り。
以降は下記のパッケージが入っている前提で説明します。

package.json
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-markdown": "^4.3.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.4.3",
    "styled-components": "^5.2.0",
    "typescript": "^4.0.3",
    "@material-ui/core": "^4.9.11",
    "@material-ui/icons": "^4.9.1",

今回の肝となっているMarkdownからwebページを生成する処理はreact-markdownを使っています。
https://github.com/rexxars/react-markdown

Webサイトの準備

今回はReactを使ってWebサイトを作成します。
サイトの公開にはVercelを使っています。

Vercelを使ってReactで作ったアプリケーションを公開する方法は公式ドキュメントに載っているのでそちらを参照してください。
Githubと連携してしまえばPushするだけでデプロイされるのでものすごく簡単ですよ。
https://vercel.com/guides/deploying-react-with-vercel-cra

create-react-appで作ったwebサイトからサンプルコードを漏れなく消す』という記事でWebサイトをReactで作って最低限の状態まで作る手順を書いているのでそちらも見ていただけると嬉しいです。

ブログページの構成

/blogsでブログページへのインデックスを表示するページを作ります。
ブログページのURLは/blogs/:titleとします。:titleのところがブログごとに変動します。

これに合わせてrouterを作ります。

src/pages/index.tsx
// ※routerの箇所のみ抜粋しています
const Blogs = lazy(() => import('./Blogs'));

<Switch>
  <Route path="/blogs" component={Blogs} />
</Switch>
src/pages/Blogs/index.tsx
import React from 'react';
import { Route, Switch, Link } from 'react-router-dom';
import { List, ListItem, ListItemText } from '@material-ui/core';
import Blog from './Blog';
// data.jsonは後ほど紹介するバッチで自動生成しているjsonファイル
// ブログのインデックス情報を定義しています
import mds from './data.json';

export default () => {
  // react-markdownの機能を使ってmarkdownからページを生成
  const blogs = mds.map((md) => (
    <Route key={md} path={`/blogs/${md}`}>
      <Blog mdPath={`/mds/${md}.md`} />
    </Route>
  ));
  // mdsを基にブログページへのリンクを生成
  const index = mds.map((md) => (
    <ListItem button component={Link} to={`/blogs/${md}`}>
      <ListItemText primary={md} />
    </ListItem>
  ));
  return (
    <Switch>
      {blogs}
      <Route>
        <List component="nav" aria-label="secondary mailbox folders">
          {index}
        </List>
      </Route>
    </Switch>
  );
};

ブログページを作成

Markdownはpublic/mds/配下に格納することにしました。
とりあえずサンプルとしてfirst.mdとsecond.mdをMarkdown形式で作りました。

public/mds/first.md
# first blog

初めてのブログ
public/mds/second.md
# second blog

2 つめのブログ

json自動更新

public/mds/配下のファイルを読み込み、インデックスページの情報源data.jsonを自動生成するバッチを作りました。

updateBlogIndex.js
const fs = require('fs');

fs.readdir('./public/mds/', (err, files) => {
  if (err) throw err;
  const convertedFiles = files.map((file) => (
    file.replace(/\.md$/, '')
  ));
  console.log(convertedFiles);

  const j = JSON.stringify(convertedFiles.reverse());
  console.log(j);
  fs.writeFileSync('./src/pages/Blogs/data.json', j);
});

上記を実行すると下記のようなjsonファイルがブログタイトル(ファイル名)の配列が書かれたjsonが生成されます。

src/pages/Blogs/data.json
["second","first"]

本番環境へデプロイ

これで実装は完了です。
かなり簡単ですよね。むしろ下手にCMS導入するより簡単です!!(もちろんCMSはもっと多機能ですがw

ブログ公開

最初に書いた通り下記の3つの手順でできます。

Markdownでブログを書く

public/mds/にmarkdown形式でブログを書きます。
現在の仕様だとファイル名がタイトルになります。

インデックスページを自動生成する

updateBlogIndex.jsを実行してインデックスページを生成します。

GitHubにPushして自動デプロイ

Vercelを使っているのでGithubの特定のブランチにPushすると自動デプロイされます。

今後やりたいこと

とりあえずMarkdownを作るだけでブログを公開するという最低限の仕組みは作れましたが、まだCMSとしては弱すぎるので下記は実装したいと思っています。

インデックス生成バッチを手動実行するのが面倒

手動実行とはえてして忘れるものです。
CIなどで自動実行させるようにして忘れないようにするべきです。

インデックスのページネーション

今は1つのページにすべてのインデックスが表示されてしまうので、ブログ数が大量に増えたら破綻します。
ページネーションさせるなどを考える必要があります。

ブログの管理方法を考える

今は名前の降順にインデックスが作成されます。
ファイルの命名規則を決めて日付をいれるなどをすれば今の仕様でも管理できるかも知れませんが、別途投稿日などを持つようにして管理しやすいようにしたい。
また、public/mds/直下に格納しているが投稿日などでディレクトリ階層を作ったほうが管理しやすくなりそう。

ブログページに投稿日を入れたい

ブログはいつ投稿されたかという情報が大事なので、ブログページに投稿日を入れるようにしたい。
ブログのファイル名などに投稿日を持たせるようにして、jsonを生成するときに日付情報を持たせるようにするなど方法を検討したい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アロー関数 =>

今まで何気なくこのアロー関数を使ってきたのですが、やっぱりJSの基礎がなっていないので気になります
私気になります(?)

アロー関数とは

=>を使ってfunctionを使わずに関数を作成することができる

具体的に見ていきましょう

function createGreeting(name) {
  return "Hello" + name;
}

こういう記述を

const createGreeting = name => "Hello" + name;

こんなに簡単に記述できる様になるんですね〜〜
ただこれは引数が1個で、処理が1行で済む場合です
2個以上2行以上になると

function createGreeting(name, greeting) {
  console.log(name);
  return greeting + "" + name;
}

こんな処理を

const createGreeting = (name, greeting) => {
  console.log(name);
  return greeting + "" + name;
}

こうやって書き換えます

ポイントは引数2個以上の場合はしっかり()の中に引数を記述すること!
後、2行以上の記述になると、{}の中に処理を記述すること!
感覚的にこの記号=>わかりやすいですよね

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryプロジェクトでもReact風に書ける?

jQueryを使ったシステム開発で、ES6+Webpack形式でコーディングしていました。

その際にjQuery.html()メソッドで追加する目的のHTML文字列を管理していたのですが、分かりやすくするために調整していたら自然とReactのJSX風になってビックリ?というお話です。

基本的な例

ファイル名と定数名は大文字で始めます。

こうすることで、hogeTemplateとかhogeElement等の命名をしなくて良くなります。
なおかつ他に大文字の定数が無いので、HTMLの塊であることは一目瞭然になるメリットがあります。

Hello.js
// 引数でinnerHTMLが変わる
export const Hello = (username) => {
  return `<p>Hello, ${username}!</p>`;
};
ErrorFeedback.js
// 引数でクラス名とinnerHTMLが変わる
export const ErrorFeedback = (error) => {
  const classname = error ? 'text-error' : 'text-success';
  return `<div class="${classname}">${error ? error.description : 'OK'}</div>`;
};

連続データの例

Reactでお馴染みのmapを使っても書けます。
まずは単純な例です。

const UserListItems = (users) => {
  return users.map((user) => `<li data-id=${user.id}>${user.username}</li>`);
};

$("ul").html(UserListItems(users));

$(selecter).html(htmlString)htmlString引数に配列を渡しても良いのかと思うかもしれませんが...実は正常に表示されます!
配列のカンマが画面に入るなんてことにはなりません。

これをもう少し詳しく見ていくと、とある注意点があります。

htmlStringが配列

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <ul></ul>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script>
      const users = [
        { id: 1, username: 'foo' },
        { id: 2, username: 'bar' },
        { id: 3, username: 'buz' },
      ];

      const UserListItems = (users) => {
        return users.map((user) => `<li data-id=${user.id}>${user.username}</li>`);
      };

      $('ul').html(UserListItems(users));
    </script>
  </body>
</html>

結果はOKです。

スクリーンショット 2020-11-04 2.05.36.png

htmlString<親要素>配列</親要素>

これはReactで頻出パターンだと思いますが、上記と同じノリでやろうとすると上手く動きません。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script>
      const users = [
        { id: 1, username: 'foo' },
        { id: 2, username: 'bar' },
        { id: 3, username: 'buz' },
      ];

      const UserList = (users) => {
        return `<ul>${users.map((user) => `<li data-id=${user.id}>${user.username}</li>`)}</ul>`;
      };

      $('#root').html(UserList(users));
    </script>
  </body>
</html>

結果... コンマも出力されます。
スクリーンショット 2020-11-04 2.09.52.png

ではどうすれば解決するのかというと、コンマを無くしてしまえばOKです。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script>
      const users = [
        { id: 1, username: 'foo' },
        { id: 2, username: 'bar' },
        { id: 3, username: 'buz' },
      ];

      const UserList = (users) => {
        return `
          <ul>
            ${users.map((user) => `<li data-id=${user.id}>${user.username}</li>`).join('')}
          </ul>
        `;
      };

      $('#root').html(UserList(users));
    </script>
  </body>
</html>

結果は無事に表示できました。

スクリーンショット 2020-11-04 2.11.07.png

おわりに

これは残念ながら一人で担当している開発のため、チームで作業する場合はアプローチが有効かは分かりませんが...
jQueryと、React風の宣言的なUI記法の狭間で彷徨っている場合には現実的な解の一つになるような気がします。

また、jQuery開発の際にhtmlをJSで管理する場合
どうやっているか等コメント頂けましたら嬉しいです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む