- 投稿日:2020-11-04T22:43:40+09:00
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.option = {//略}
などでも動いたが、怖いのでコピーを作成React Datepicker
https://reactdatepicker.com/
https://github.com/Hacker0x01/react-datepicker#localization
- 投稿日:2020-11-04T21:07:49+09:00
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/_/nodeDockerfileFROM node:15.0.1-alpine3.10docker-compose.ymlの作成
https://docs.docker.com/compose/compose-file/ からテンプレートを取得し、ゴニョゴニョします。
#docker-compose.ymlの作成 vi Docker-compose.ymldocker-compose.ymlversion: "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 typescriptfront/my-app配下に、Reactのプロジェクトが作成されたことを確認できたら、
再度Dockerfileと、docker-compose.ymlファイルを修正し、コンテナを立ち上げます!DockerfileFROM node:15.0.1-alpine3.10 #下2行を追記! COPY ./front /front WORKDIR /frontdocker-compose.ymlversion: "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/coreinstallできたら、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.tsximport React from "react"; import { Button } from "@material-ui/core"; const WrapButton = () => { return ( <> <Button variant="contained" color="secondary"> Hello World! </Button> </> ); }; export default WrapButton;App.tsximport 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;ちょっと不格好ですが、Buttonコンポーネントが表示できていることが確認できましたね!(メールの通知は気にしないでください・・笑)
ひとまず開発環境が整ったので、ここから好きなようにゴニョゴニョしていきましょう!
- 投稿日:2020-11-04T13:41:25+09:00
Reactを学ぶV(コンポーネント間でデータの受け渡しをする)
■ はじめに
タイトルについて記事にしました。
この記事で得る内容は以下の通りです。・コンポーネントの概要
・コンポーネントを使う理由
・コンポーネントの種類
・propsでデータを受け渡すには
・受け渡せるデータ型■ コンポーネントとは(復習)
UIは2つに分類される
1.見た目(View)
2.機能(Controller)コンポーネントとは、見た目と機能を合わせ持ったもの
■ なぜコンポーネントを使うのか
・ 再利用するため
→コンポーネントにprops(引数みたいなもの)を渡す事で、例えばログイン・ログアウトボタンを作る時にボタンだけコンポーネントを作っておき、文字だけを変えて再利用するなど
・ 分割統治するため
→コンポーネントを分けておくことにより、管理がしやすくなる
・ 変更に強くするため
→ コンポーネントが分かれていることにより、他に影響を与えず変更に対応できる
■ コンポーネントの種類
・ Class Component
・ クラスによって管理されたコンポーネント
・ React.Componentを継承
・ ライフサイクルやstateを持つ
・ propsにthisが必要
・ renderメソッド内でJSXをreturnするsrc/Article.jsximport 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.jsximport 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という形で受け取って
呼び出して、こんな感じで表示できる
■ 受け渡せるデータ型
Blog.jsximport 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.jsximport 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;それぞれ異なる値で渡されて表示される
更に、一例ですが記事タイトルの下に順番を表示したい場合は、
Article title
の横にorder
と定義してBlog.jsximport 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.jsximport React from "react"; const Article = (props) => { return ( <div> <h2>{props.title}</h2> <p>順番は{props.order}です</p> </div> ); }; export default Article;この様に表示することができ、繰り返し書かず、変更に強いという特徴が分かる
- 投稿日:2020-11-04T12:38:15+09:00
Firebase+Reactで会員登録機能を作る際に、ユーザー名(username)も一緒に登録したい!
Firebase Auth react
やfirebase react
で検索すると、基本的なemail+passwordでユーザー登録する機能の作り方
は出くるのですが、ユーザー名も一緒に登録する方法を調べていても一向にしっくりくる情報が得られないんですよね。。そこで、この記事では
firebase+reactでユーザー登録の機能を作る際に、ユーザー名もセットで登録する方法
について解説します。実装の全体像
まずは全体像を掴みましょう。
firebaseでユーザー登録
といえば、Authenticationですよね。以下の画像のように、様々な認証方法があってとても便利な機能です。
ただ、初心者の僕はここで疑問が浮かびます。
「Authenticationでメールアドレスとパスワード、ユーザーのIdが管理できるのはわかった。だけど、ユーザー名を登録したいと思ったらどうすれば良いんだろう??」
僕はこの疑問についてずっと考えたのですが、良い答えに辿り着けず数日が経過したある日、ふと思いました。
「もしかしたら、Firestore使えばいけるんじゃね?」
そうです。firebaseでユーザー名を登録する場合はAuthenticationではなくfirestoreにデータを保存する必要があったのです。
この
Authenticationでどうすればユーザー名登録できるか
みたいな考えに束縛されて、答えに辿り着けずにいました。具体的にどうすれば良いのか
前述の通り、
firestore
にdb.collection('users').doc(uid).set(userInitialData)
を使ってusername
、password
のデータを保存します。(後述します)ユーザーを追加する方法を解説
仮に、
password
だけでユーザー登録するとしたら、特にfirestoreを使う必要はなく、createUserWithEmailAndPassword
でできるのですが、ユーザー名(username)
を登録するとなると、firestoreを使う必要があります。まずは、お好みの形でFormを作成してください。
僕の場合は、Bootstrapを使っているので、
SignUp.jsx
ファイルは以下のようになりました。
今回特にみて欲しいのはhundlesubmit
の部分です。SignUp.jsximport 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.jsxconst 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
の中に入っているusername
、uid
を取り出しました。(createUserWithEmailAndPasswordはfirebaseの公式のリファレンス(英語)にメソッドの返り値や引数などの詳細が載っています。)
そして、
firebase.firestore().collection('users').doc(uid).set(userInitialData)
でコレクションにデータを保存します。
繰り返しになりますが、users
というコレクションの、uid
をIDにもつドキュメントに、先ほど取得したuserInitialData
を.set()
メソッドで登録します。これで完了です??
参考
- 投稿日:2020-11-04T11:43:05+09:00
【React】Material-UI 導入 〜 運用手順
Material-UIインストール
ターミナルの開発プロジェクト下で Material-UI の実装に必要なパッケージをインストールします
npm install --save @material-ui/core @material-ui/icons @material-ui/systemMaterial-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/
- 投稿日:2020-11-04T09:07:43+09:00
MarkdownをGitHubに上げるだけでブログを公開できる仕組みをサクッと作ってみた
世の中には便利なCMSプロダクトがたくさんありますが、ぶっちゃけエンジニアだったらWeb上のフォームでコンテンツを入力するより、ローカルでMarkdownを作ってサクッとGitHubにPushするだけでブログ公開できたほうが楽だなーと思ったので作ってみました。
できるようになること
下記のようなインデックスページとブログページをMarkdownを作るだけで生成できるようになります。
ブログ公開の手順は下記の通りでとても簡単です。
- Markdownでブログを書く
- インデックスページを自動生成する
- 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-markdownWebサイトの準備
今回は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.tsximport 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.jsconst 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を生成するときに日付情報を持たせるようにするなど方法を検討したい。
- 投稿日:2020-11-04T09:01:29+09:00
アロー関数 =>
今まで何気なくこのアロー関数を使ってきたのですが、やっぱり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行以上の記述になると、{}
の中に処理を記述すること!
感覚的にこの記号=>
わかりやすいですよね
- 投稿日:2020-11-04T02:12:48+09:00
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です。
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>ではどうすれば解決するのかというと、コンマを無くしてしまえば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>結果は無事に表示できました。
おわりに
これは残念ながら一人で担当している開発のため、チームで作業する場合はアプローチが有効かは分かりませんが...
jQueryと、React風の宣言的なUI記法の狭間で彷徨っている場合には現実的な解の一つになるような気がします。また、jQuery開発の際にhtmlをJSで管理する場合
どうやっているか等コメント頂けましたら嬉しいです!