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

Reactで作ったアプリをHerokuにデプロイする時にやること!!

こんにちは!
私は、作り終わったアプリをいつもHerokuにデプロイするのですが、大抵どっかでつまづいてしまうんですよね。
っていうことで、今日は、Reactでアプリを作った時のHerokuでのデプロイについて、私自身の実体験をもとに備忘録も兼ねてまとめてみます!

1.Heroku(ヘロク)に登録

まずは、以下のサイトに行ってアカウント登録をします。
https://jp.heroku.com/

今回は、登録の仕方の詳細は、端折らせて頂きます。

2.Create New Appする

それでは、引き続きHerokuで作業していきましょう。
下の画像のような、Newというボタンがあるので、クリックして、Create New Appします。
スクリーンショット 2020-08-08 15.47.38.png
すると、App nameの入力を求められます。お好きな名前を付けてください。(ただし、大文字などいくつか使えない文字や記号もあります。)
もし、この部分を空欄にしてしまっても、Herokuがテキトーに名前を付けてくれるので大丈夫です!
Choose a regionはUnited StatesのままでOKです。
フォームの最後にあるCreate appボタンを押せば、これでアプリケーションをデプロイするための最初の準備ができました!

3.アプリを作成する

ここでは、Reactを使って作りたいアプリを1つ作ってください。
むしろHello Worldでレンダリングするだけのページでも構いません。
作り方の解説は端折ります。

4.さぁ、デプロイしてみましょう!

おかえりなさい!いよいよデプロイです!
ターミナルを開いて、以下のコマンドを入力してください。

 heroku login 

q以外で好きなキーを押すように指示が表示される。好きなキーを押します。
すると、ブラウザが開いて、Log inと書かれたボタンが中央にあるのでクリック。
これで、ターミナルからHerokuへのログインができました。
それでは、作ったアプリを保存してあるフォルダに移動してください。

 cd 作ったアプリを保存してあるフォルダ

そして以下のコマンドを入力してください。

git init
/* Reinitialized existing Git repository inと出てきます。
in以降は、GitHubのリポジトリ名とかになってます。*/

heroku git:remote -a Herokuで登録したまたは与えられたApp name
/* set git remote heroku to っていうのが出てきます。
文字に色ついてたりしますが問題ないです。*/

git add . // addとピリオドの間に半角スペースを忘れない!

git commit -am "make it better"
/*  1 file changed, 2 insertions(+), 1 deletion(-)のような感じで、
変更したファイルの数が出てきます。何も変更していなければ、nothing to commit
って出てきます。*/

git push heroku master

少し時間がかかりますが、下にズラーと文字とか並びますが気長に待ちましょう。

5.ここから注意!エラー発生!?

Reactで作ったアプリをデプロイする時、git push heroku masterした後にエラーが起こりがちです。

! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/xxx.git'

上記のエラーが出てくることがあります。(xxxはみなさんのHerokuでのApp nameです。)
ブラウザのHerokuに行って、Overviewページに移動します。そこでBuild faildと赤字で書かれているものが、ページ右半分の一番上に表記されているはずです。その項目の、View build logというリンクをクリックして見てみます。すると、buildで何が起こったのかを調べることができる「ログ」が書かれています。
その「ログ」の最後の方までスクロールしてください。以下のような文章が見つかると思います。

Some possible problems:

       - Node version not specified in package.json
         https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version

       - A module may be missing from 'dependencies' in package.json
         https://devcenter.heroku.com/articles/troubleshooting-node-deploys#ensure-you-aren-t-relying-on-untracked-dependencies

/* 2つ目の、A moduleの文章は出てこない人もいると思います。
パッケージやライブラリの使用状況による!*/

6.A module may be missing from 'dependencies' in package.jsonに対する解決策

エディタで、package.jsonを開き、上から4番目くらいにある"dependencies"という項目を見てください。そこできちんと使っているライブラリやパッケージの名前とバージョンの漏れがないか確認しましょう。このエラーが出ている人は、漏れがある可能性があるので、使っているのに書いていないパッケージなどがあれば、漏れなく追加しましょう。

7.Node version not specified in package.jsonに対する解決策

「ログ」の上の方を見てみると、lock fileをアップデートしてくださいって出てくるので、以下のコマンドを実行します。

npm install

忘れないうちに、ブラウザ上のHerokuのSettingsページにある、Buildpacksに次のBuildpackを追加しておきます。

  • heroku/nodejs
  • https://github.com/mars/create-react-app-buildpack

Add buildpackというボタンをクリックして上記の2つを追加します。
heroku/nodejsというBuildpackは選択肢にあります。
2番目のBuildpackは、このようにURL表記で入力欄に入力してください。

上記の工程を終了したら、もう一度、git add .の部分からデプロイに挑戦してみてください。

それでもダメな場合、package.jsonの"scripts"の"start"部分を次のように変更してください。

"scripts": {
    "start": "node index.js", // 左のように、node index.jsにする!
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

以上です!!
長くなりましたが、お読み頂きありがとうございました!
不備等あれば、コメントでお知らせください!

参考にさせて頂いた投稿

Reactアプリケーションをherokuにデプロイする方法

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

Reactでhookに変えたので、useReducerを使って別コンポーネントの値を変更するサンプル

環境

  • React 16.8
  • React UI 4系
  • TypeScript 3.9.7

状況

複数のコンポーネント間で値を共有させたい

コード

TODO イメージで書いたので、動作検証をしていない。

Reducer、Context

const CatContext = createContext({} as ContextType<any>);
const reducer = (stage: CatType, action: {type:string,payload:CatType}) => {
  switch (action.type) {
    case 'init':
      return action.payload;
    case 'save':
      return action.payload;
    default:
      return state;
  }
};

reducerでは、それぞれのタイプ毎に返す値を変更する。今回は参考程度に書いたので適当にデータそのものを返すだけにしている。

type

type CatType = {
  id: number, name: string, address: number
};

typeを定義。

Parent

function Cat(props: { cat: CatType }) {
  useEffect(() => dispatch({type:'init',payload:props.cat}),[props.cat]);
  const [state, dispatch] = useReducer(reducer, props.cat);
  return (
    <React.Fragment>
      <div><Address cat={cat}/></div>
      <div><CatDetail cat={cat}/></div>
    </React.Fragment>
  );
}

このコードは、親のコンポーネントになる。そのため、初期化処理のみを行う。

Static Child

function Address(props: { cat: CatType }) {
  const { state, dispatch } = useContext<any>(CatContext);
  return (
    <React.Fragment>
      <div>{props.cat.address}</div>
      <div>{props.cat.name}</div>
    </React.Fragment>
  );
}

このコンポーネント内の {props.cat.name} という部分のデータも別のコンポーネントのデータが変わったタイミングでちゃんと反映したい。

Dynamic Child

function CatDetail(props: { cat: CatType }) {
  const { state, dispatch } = useContext<any>(CatContext);
  const [name, setName] = useState(props.cat.name);
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setName(event.target.value);
  const handleSave = () => {
    // patchリクエストでデータ更新するなど

    const payload = state as CatType;
    payload.name = name;
    dispatch({type: 'save', payload: payload});
  };
  return (
    <React.Fragment>
      <TextField onChange={handleChange} value={name}/>
      <IconButton aria-label="save" color="primary">
        <SaveIcon onClick={handleSave} />
      </IconButton>
    </React.Fragment>
  );
}

例えば、 name を変更して、ボタン押下したタイミングでデータの永続化処理が行われると仮定し、その後成功した場合、 dispatch を呼んでやる。Reduxをやっていた方は挙動がわかりやすいと思う。

注意点

  • この記事は、フロントエンドエンジニアが書いてません
  • 同コンポーネント内のデータを変更するだけなら、状況によるが、useStateを使う方がわかりやすい
  • ステートを直接変更しない(データそのものを扱うようにする)
    • reducerでstateを変更しないこと
  • useEffectにdepsそのものが変わった時だけデータを購読するようにする。レンダリング後に毎回実行しなくても良い。逆に変更の要件がないなら、depsには[]を渡すことでクリーンを保つことができる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.js + MDXで作成した静的サイトのレイアウトをカスタマイズする

Next.js + MDXでブログを構築した際、
レイアウトをカスタマイズするのに試行錯誤したのでその結果を備忘録として残しておく。

サンプルコードはこちら

MDXで記述したページに共通のヘッダやフッタを配置したい

ブログの各記事に、
記事のメタ情報(タイトル、作成日、カテゴリ ...)を記載したヘッダや
関連記事へのリンクを含むフッタなどの共通コンポーネントを配置することを考える。

一番シンプルな方法は各 MDX ファイル内に共通コンポーネントの呼出しを記述する方法。
けれど、すべての MDX ファイルに以下のような記述をするのは DRY 原則に反するのでやりたくない。

import CommonHeader from "@/components/CommonHeader";
import CommonHeader from "@/components/CommonFooter";

<CommonHeader />

ここに本文を書く。

<CommonFooter />

そこで、このブログでは以下のような方法で実装した。各記事のパスは /blog/{year}/{month}/{post_id} とする。

  • Next.js の Dynamic Routes を利用し、
    共通コンポーネントを記述したページ pages/blog/[...id].tsx を作成する。

  • babel-plugin-import-glob-array
    用いて MDX ファイルを一括インポートし、各 MDX ファイルのパス情報を配列形式で取得する。

  • 取得したパス情報をもとに SSG 用のパス情報を生成する。また、
    Dynamic Import 機能で
    MDX ファイルをパスごとに取得して、コンテンツとして埋め込む。

実装の一部を以下に抜粋する。詳細はサンプルコードを参照。

FrontMatter を用いたメタ情報の取り扱いについては
公式のサンプルコードもある。

// [...id].tsx
import React from "react";
import { GetStaticPaths, GetStaticProps } from "next";
import dynamic from "next/dynamic";
import mdxUtil from "@/lib/mdx-util";
import BlogLayout from "@/layouts/BlogLayout";

interface Props {
  resourceId: string;
  frontMatter: FrontMatter;
}

const Post: React.FC<Props> = (props: Props) => {
  const { resourceId, frontMatter } = props;
  // MDX ファイルを dynamic import してコンテンツとして埋め込む
  const MDX = dynamic(() => import(`@/posts/${resourceId}.mdx`));
  return (
    <BlogLayout frontMatter={frontMatter}>
      <MDX />
    </BlogLayout>
  );
};

export const getStaticPaths: GetStaticPaths = async () => {
  // babel-plugin-import-glob-array でパス情報を取得、パスリストを生成
  const posts = await mdxUtil.getPosts();
  const paths = posts.map((post) => {
    return {
      params: { id: post.resourceId.split("/") },
    };
  });
  return { paths, fallback: false };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const resourceId = (params.id as string[]).join("/");
  // 各パスに対応する MDX ファイルのメタ情報とパス情報を Props として渡す
  const post = await mdxUtil.getPostByResourcePath(resourceId);
  return {
    props: {
      resourceId,
      frontMatter: post.frontMatter,
    },
  };
};

export default Post;

MDX 本文中の各コンポーネントに独自のスタイルを適用したい

MDXPrivider を用いることで、
MDX 本文中の各要素をカスタムコンポーネントと対応づけることができる。
あとはカスタムコンポーネントに好きなスタイルを適用すればよい。

import React from "react";
import { MDXProvider } from "@mdx-js/react";
import Link from "next/link";
import Paragraph from "@/components/Paragraph";

interface Props {
  frontMatter: FrontMatter;
  children: React.ReactNode;
}

const BlogLayout: React.FC<Props> = (props: Props) => {
  const { children, frontMatter } = props;

  // MDX 中の要素をカスタムコンポーネントに対応づける
  const state = {
    p: Paragraph,
  };

  return (
    <>
      <h1>{frontMatter.title}</h1>
      <span>Created at {frontMatter.date}</span>
      <MDXProvider components={state}>{children}</MDXProvider>
      <Link href="/">
        <a>Go back to home.</a>
      </Link>
    </>
  );
};

export default BlogLayout;

感想

Next.js の Dynamic Routes と MDX を併用する実装はもう少しきれいにできないだろうか。

babel-plugin-import-glob-array を使ったり dynamic import を使ったり、
冗長な感じがして気になってはいるが、今のところ他にうまい方法が思いついていない。

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

[React] propsの基本

前回は関数コンポーネントの基本的な書き方について記事を書いた。
https://qiita.com/QiitaD/items/5ba8adbde6ff01c914a7

今回はpropsについて記事にしようと思う。

propsとは

データの入ったプロパティという意味。個人的には引数みたいなものと認識している。
例えば関数コンポーネントにpropsとしてデータを渡すことで、そのデータを関数コンポーネント内で使うことができるし、データに応じた要素を返却できる。

コード

前回のButton.tsxファイルにpropsを設定してみる。propsとしてボタンに表示するテキストを指定できるようにしようと思う。

Button.tsx

import React from "react";

export default Button;

// propsを指定するときはinterfaceを使う。このブロック内にボタン表示テキストを定義しておく
interface ButtonProps {
  text: string;
}

//関数コンポーネントの引数としてButtonPropsを指定することで、呼び出し側でtext: stringを指定しなければならなくなる。(もちろん複数定義されていればその分渡す必要がある)
function Button(props: ButtonProps) {
  return <button>{props.text}</button>;
}

呼び出し側は、関数呼び出し時にButtonPropsに定義されている変数を渡すだけである。

import React from "react";
import { RecoilRoot } from "recoil";
import Button from "./Button";

function App() {
  return (
    <RecoilRoot>
      <div>
        //ここでtextにデータを渡す。
        <Button text="テストボタン"></Button>
      </div>
    </RecoilRoot>
  );
}

export default App;

表示結果

前回と同じ結果になる
スクリーンショット (110).png

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

[React] 関数コンポーネント

関数コンポーネントとは

UIの一部を部品としてコンポーネント化することで、再利用できるようになる。
例えば、テキストを引数によって変更できるボタンをコンポーネント化する、のような感じである。

コード

今回はボタンコンポーネントを作成してみる。
まずはファイルを作成する。名前は適当に「Button.tsx」とする。

Button.tsx

import React from "react";

//他のファイルでこのファイルをimportすることで、ここの関数を使えるようになる
export default Button; 

//この関数を呼ぶことでボタンが返却される
function Button() {
  return <button>テストボタン</button>;
}

上記の関数を他のファイルで呼び出すときは以下のように書く。

App.tsx

import React from "react";
import { RecoilRoot } from "recoil";

//「Button」という名前でButton.tsxをimportする。名前は好きに付けることができる。
//fromの後はButton.tsxへのパスを書く。今回はApp.tsxと同じ階層にあるので以下のようにしている。
import Button from "./Button";

// import文で付けた名前のタグで関数コンポーネントを呼び出せる
function App() {
  return (
    <RecoilRoot>
      <div>
        <Button></Button> 
      </div>
    </RecoilRoot>
  );
}

export default App;

表示結果

ボタンが表示される
スクリーンショット (110).png

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

firebase onAuthStateChangedを使ってユーザーのログイン状態をリアルタイムでリッスンするカスタムフック

メリット

レンダリング時や、firebase(authentication)のcreateUser..., signInWith..., signOut などのメソッドを呼び出した時、このフックがstateを変更し、ログイン、ログアウトなどの表示をリアルタイムに切り替えることができる。

コード

Button.jsx
import { useCurrentUserStatus } from '../custom_hook';

const Button= () => {
  const isSignedIn = useCurrentUserStatus(); 

  return (
    {isSignedIn ? (
      <button onClick={logout}>ログアウト<button>
    ) : (
      <button onClick={login}>ログイン<button>
    )}
  )
}

export default Button;
useCurrentUserStatus.jsx
import { useState, useEffect } from 'react';
import AuthAPI from '../AuthAPI';

const useCurrentUserStatus = () => {
  const [isSignedIn, setIsSignedIn] = useState(null);

  useEffect(() => {
    function handleStatusChange(user) {
      setIsSignedIn(user !== null);
    }

    const unsubscribe = AuthAPI.subscribeCurrentUserStatus(handleStatusChange);

    // onAuthStateChangedメソッドはメモリを確保するため、何度も呼ばれてメモリリークが発生しないように
    // アンマウント時にクリーンナップ関数を呼ぶ必要があるらしいです。 あんまり詳しくないです。
    return () => unsubscribe();
  });

  return isSignedIn;
}

export default useCurrentUserStatus;
AuthAPI.js
// appは別ファイルで初期化したものをインポート
import app from './config';

class AuthAPI {
  constructor() {
    this.auth = app.auth();
  }

  subscribeCurrentUserStatus(handleStatusChange) {
    return this.auth.onAuthStateChanged(user => {
      handleStatusChange(user);
    });
  }
}

export default new AuthAPI();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンジニア未経験のキャリアコンサルタントによるFrontend学習備忘録(1)

突然ですがReactの勉強始めました!

もうタイトルから情報が盛りだくさんですが、私はエンジニアではありませんしエンジニアを目指しているわけでもないですが、純粋にプログラミングが好きな非エンジニアです。
仕事ではGASを使って業務効率化を進めたりしていますが、開発の経験は皆無です。

そんな私が突如知り合いのサービス開発にフロントエンドエンジニアとしてJOINすることになったので、その学習の記録をつけて行きたいと思います。
しかし、基礎文法からゆっくり学習している時間はないので、その都度必要になったことを記録していきます。

そもそも型とはなんぞや

「TypeScriptは基本JavaScriptだから大丈夫」という言葉を信じてコード読み始めましたが、RubyとJSしか触ったことがない自分に取って型宣言とは非常に慣れない!
今回は「型」について調べた情報をまとめていきます。

型とは単位みたいなものという説明が一番しっくりきました。値に関するメタ情報のことで、コンピュータにはとってデータがどんな情報なのかを型というもので定義して教えてあげるのだと。

では早速TypeScriptで型定義の記述の仕方と型の種類について残していきます!

文法

String (文字列)型

二重引用符(")または単一引用符(')を使用して文字列データを囲むと文字列として認識される

let color: string = "blue";
color = "red";

//color 変数名
//string 型
//red 値

Number (数値)型

10進数とか16進数とかわかりません。とりあえず数値が入れられる型です。
number型に文字を入れるとErrorになります。
これが型定義の強み!!

let decimal: number = 6;
decimal = "文字";
=> Error

Boolean(論理)型

true / falseの2つの値しか入りません。
実際のコードの中ではon/offのスイッチに使われているのを見かけた。なるほど。。

let isDone: boolean = false;

Array(配列)型

おなじみの配列型ですね。インデックス番号により複数値を格納できる型です。

let list: number[] = [1, 2, 3];
list[0]
=> 1

let animals: string[] = ["cat", "dog", "mouse"];
animals[1]
=> dog

リテラル型

今までStringとかNumber(int)型は知っていたので抵抗はなかったが、実際のコードだけ見たときに「知らない型出てきた〜」と一番焦ったのがこれ
言われてみれば納得だが、型定義する型も定義できるらしい。
Stringリテラル型は定義した型に設定した値のみ格納可能

// Answer というリテラル型を定義する
type Answer = `yes` | `no`;

// Answer 型の変数には 'yes' か 'no' のみ格納可能
let answer: Answer
answer = 'yes';    // OK
answer = 'no';     // OK
answer = 'maybe';  // Error
answer = 100;      // Error

今回実務で?となったこと

iconColorSchema.ts

export const iconColorSchema = [
  {
    type: 'tool',
    color: '#1890FF',
  },
  {
    type: 'bgcolor',
    color: '#fafafa',
  },
]

//iconColorSchema 変数名
//[{}]配列の中にオブジェクトを格納(colorをkeyに値を取得する)
//あれ?これってtypeに定義されているtoolが型?

iconCompornent.ts

import React from 'react'
import { iconColorSchema } from './iconColorSchema'

const IconConpornent = () => (
  <IconCompornent
    maincolor={iconColorSchema[0].color}
    subcolor={iconColorSchema[1].color}
  />
)

最後に

ちょっとまだ知識が追いついてないこともありますが、少しづつ勉強進めていきます!

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

React Hooks inputのコンポーネントの作り方

色々とコンポーネント化していくと(inputのtypeを変更できる汎用性を持った)テキスト入力だけのコンポーネントをも欲しくなった。

しかしそうなると親で値を管理したいはずなので面倒だなぁって思っていましたが意外と楽だった。

概要

親から子に値を渡してもReadしかできないので親のuseStateの値更新する関数をそのまま子コンポーネント に渡す。

子コンポーネント

...any使っちゃいました。

import styles from './FormText.module.scss'
import { useState } from 'react'

export const FormText = ({
  className,
  type,
  label,
  onChange,
}: {
  className?: string
  label: string
  type: string
  onChange: (e) => void
}) => {
  const [name, setName] = useState('')
  const handleSetName = (e: React.ChangeEvent<HTMLInputElement> | any) => {
    if (onChange) {
      onChange(e.target.value)
    }
    setName(e.target.value)
  }
  return (
    <>
      <div className="form_text">
        <label htmlFor="">{label}</label>
        <input type={type} value={name} onChange={handleSetName} />
        {name}
      </div>
    </>
  )
}

親コンポーネント

親コンポーネントでは通常のinputを使っているのと変わらないような感じでonChangeのとこにuseStateの値をセットする関数をいれる。

これでtypeがtextとpassword別々で値を管理できる?

import { useState } from 'react'
import Head from 'next/head'
import Layout, { siteTitle } from '../components/layout'
import { getSortedPostsData } from '../lib/posts'
import { GetStaticProps } from 'next'
import { Section } from '../components/Section'
import { Sheet } from '../components/Sheet/Sheet'
import { FormText } from '../components/FormText/FormText'

export default function Home({
  allPostsData,
}: {
  allPostsData: {
    title: string
  }[]
}) {
  const [name, setName] = useState('')
  const [passWord, setPassWord] = useState('')

  return (
    <Layout home>
      <main>
        <Section>
          <Sheet>
            <FormText label="text" type="text" onChange={setName} />
            <FormText label="text" type="password" onChange={setPassWord} />
          </Sheet>
        </Section>
      </main>
    </Layout>
  )
}

export const getStaticProps: GetStaticProps = async () => {
  const allPostsData = getSortedPostsData()
  return {
    props: {
      allPostsData,
    },
  }
}

ひとこと

いつもevent系で型の解がわからないです...。
わかる人がいれば教えていただきたいです。

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