20200909のReactに関する記事は11件です。

Expo x React Native DebuggerでUIデバッグ

概要

SimulatorでWeb開発でやるようなInspectを出来るようにする方法を書いておきます。

React Native Debuggerをインストール

caskでreact-native-debuggerをインストールします。

$ brew update && brew cask install react-native-debugger

React Native Debuggerの設定をExpo用に変更

React Native Debuggerを開いて、Debugger > Open Config Fileを選択します。

image.png

.rndebuggerrcという設定ファイルが開かれるので、defaultRNPackagerPortsをExpo用に19001に変更します。

- defaultRNPackagerPorts: [8080]
+ defaultRNPackagerPorts: [19001],

デバッグ方法

上記までが終わったら、一度expoを再起動します。

これしなくてエラーが出た

再起動したら、Simulatorを立ち上げます。
以下はiOS Simulatorで進めます。

SimulatorでDebug JS RemotelyをONに

Simulator上でCmd + Dでメニューを開きます。
すると以下のようにメニューが出るので、Debug Remote JSをタップします。

image.png

React Native Debuggerを起動

React Native Debuggerを起動して、iOS Simulatorをリフレッシュすると以下のような小さいメニューが出るので、InspectをタップするとWeb開発のようなInspectが出来るようになります。

image.png

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

Reactファイルをwebpackでバインドしてlocalhostで確認してみる

Reactとwebpackとyarnを試しに使ってみる

本記事はタイトルの通り、様々なサイトや記事を参考にReactで作ったファイルをwebpackでバインドしてlocalhostで確認してみた一連の流れを記述してあります。使用するReactファイルはReact公式チュートリアルパクって参考にして作成したものになります。

create-react-appは使用していません。

参考にさせていただいたサイトや記事は最後に記載してあります。

yarn(npm)でモジュールを準備する

yarn add コマンドは、npm install --saveに対応しています。

yarn init
yarn add react react-dom
yarn add toastr
yarn add webpack webpack-cli webpack-dev-server
yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react

今回は便宜上3回に分けてインストールしましたが、一度にインストールすることも可能です。

  • 3行目
    • toastrは今回使用するreactファイルでtoastrというライブラリを使用しているためインストールしています
  • 5行目
    • babel-loaderはwebpackでbabelを使用するために必要になるようです。cssを扱うならばcss-loaderが、sassならsass-loaderが必要になります
    • @babel/core は、babelの本体になります
    • @babel/preset-envはECMAScript用presetで、書くブラウザのターゲットバージョンなどを指定できます。今回は未指定なのでes5の構文に変換されています
    • @babel/preset-reactはReact用presetです。TypeSctiptを扱う場合は@babel/preset-typescriptが必要になります

webpackの設定ファイルを作成する

これまででとりあえず必要なモジュールのインストールが完了しました。使用するjsファイル、htmlファイルもあとでまとめてダウンロードするので、次はwebpackの設定ファイルであるwebpack.config.jsを作成します。

webpack.config.js
module.exports = {
  // 本番はproduction 開発環境はdevelopment
  mode: 'production',
  // entryポイント(相対パス)
  entry: './src/javascripts/index.js',
  // 出力先(絶対パス)
  output: {
    path: `${__dirname}/dist`,
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        // loaderの適応ファイルを指定する正規表現
        test: /\.js$/,
        use: [
          {
            // Babelを使う
            loader: 'babel-loader',
            // Babelのオプションの設定
            options: {
              presets: [
                //presetの設定
                '@babel/preset-env',
                '@babel/preset-react',
              ]
            }
          }
        ]
      }
    ]
  }
};

各設定の詳細はソースのコメントのとおりです。

次章はwebpack.config.jsの記載内容と矛盾が出ないようエントリーポイントに正しくjsファイルを配置します(本来はwebpack.config.jsに記載するパスを合わせる)。

Reactファイルの配置

githubにおいてあるファイルを./src/javascripts/にコピーするか、./src/に移動して以下のコマンドを打ち込んでください。

git clone git@github.com:Asa-to/javascripts.git

以上のコマンドを打ち込むことで、カレントディレクトリにjavascriptsフォルダがコピーされます。

最後に、下記のindex.htmlを作業ディレクトリに配置したら準備は完了です。

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>
    <div id="root"></div>
    <script src="./dist/main.js"></script>
</body>
</html>

バインドして結果を確認する

ここまでで以下のようなファイル配置になっていれば準備は完了です。早速バインドしてみましょう。

./
 ├ index.html
 ├ src
   ├ javascripts
 ├ node_modules
 ├ webpack.config.js
 ├ package.json

バインドコマンドは以下です。

./node_modules/.bin/webpack

以上でコマンドが正常に実行されれば、./distにバインドされたファイルが出力されているはずです。./dist/main.jsが生成されていることを確認してください。

./node_modules/.bin/webpack-dev-server

続いて以上のコマンドを実行してブラウザからlocalhost:8080にアクセスしてみます。
Screenshot.png

以上のような表示が確認できれば完璧です。

参考文献

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

超初心者のReact(create-react-app)基礎学習覚え書き③(Hooks)

ReactHooksでstateを扱う

Hook(フック)とは

・クラスの機能(stateやライフサイクル)をFunctional Componentでも使えるというもの
・100%後方互換で、古い書き方のコンポーネントにReactHooksを使用したとしても影響を与えない

Hook使用のメリット

シンプルさを保てる為

クラスコンポーネントが複雑(難しい)
1.this.が入ることで他言語と違った挙動で使いづらい為、トレンド的に排除されようとしている
2.stateのロジックが複雑(stateだらけだと意味不明になる)
3.複数のライフサイクルメソッドにまたがる処理をまとめて記述できる

Functional Componentでstateを使ってみる

・ステートフックと呼ばれる
・クラスコンポーネントでの
 this.stateとthis.setstate()の役割
・複数のstateを扱うときはstate毎に宣言する必要がある

useState()の使い方例

1.関数のインポート
import React, {useState} from 'react';
2.宣言
const [state変数名, state変更関数名] = usestate(state初期値);
//実際には↓
const [isHoge, setIsHoge] = useState(false);
3.JSX内で使う
<input /**中略 */ onClick={() => setIsHoge(!isHoge)} />

Functional Componentでstateが使えると何がいいのか?

classコンポーネントを減らす為に上の階層がclassコンポーネントになることが多かった。それによりpropsをどんどん渡していかなければいけなかった為にコードが複雑化しやすかった。
Hookにより末端のコンポーネントでstateを持つことにより管理がしやすくなった。

ReactHooks 関数コンポーネントでライフサイクルを扱う

useEffect()を使用する

メリット

・ライフサイクルの代替が可能
・Functional Componentでライフサイクルを使える(記述が減らせる)
・コードを(classComponentよりも分かりやすく)まとめれる
 →時系列ベースではなく機能ベースでまとめが可能

useEffect()の仕組み

・レンダーごとにuseEffect()内の処理が走るようになる
・メソッドの代替が可能
 compnentDidMount()
 compnentDidUpdate()
 compnentWillUnmount()

基本的な使い方

①レンダーごと

・基本形でuseEffect()内にCallback関数で書く
・Callbackはレンダーごとに呼ばれる
・returnするCallback関数はアンマウント時に呼ばれる
 (クリーンアップ関数とも言う)

useEffect(() => {
  console.log('Render!')
  return () => { //アンマウント時のみ
    console.log('Unmounting!')
  }
})

②マウント時のみ実行させる

・compnentDidMount()の時にだけ書いていた処理を実行するパターン
・第2引数に空の配列を渡すとマウント時(最初の一回)だのみ実行される
 →前回レンダーと今回レンダーで比較している
 (変更があった場合のみCallback関数を実行)

useEffect(() => {
  console.log('Render!')
}, [])

③マウント、アンマウント時に実行する

・①と②の複合系

useEffect(() => {
  console.log('Render!') //マウント時のみ実行される
  return () => { //アンマウント時はクリーンアップ関数として実行される
    console.log('Unmounting!')
  }
}, []) //Updatingの期間は配列が空の為、実行されない

④特定のレンダー時

・useState()と使われることが多い

const [limit, release] = useState(true);

useEffect(() => {
  console.log('Render!') //limitの値を変更(true→false)時に実行される
}, [limit])

いいねボタンの実装

LikeButton.jsx
import React, {useState, useEffect } from 'react';

const LikeButton = () => {
  const [count, counter] = useState(0);
  const [limit, release] = useState(true);

  const countUp = () => {
    counter(count + 1)
  }

  useEffect(() => {
    document.getElementById('counter').addEventListener('click', countUp)
    if (count >= 1000) {
      counter (0)
    }
    return () => {
      document.getElementById('counter').removeEventListener('click', countUp)
    }
  }, [limit]);

  return(
    <>
      <button id={"counter"}> ? {count}</button>
      <button onClick={() => release(!limit)}>もっと!!</button> {/*limitを解除するためのボタン*/}
    </>
  )
}

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

超初心者のReact(create-react-app)基礎学習覚え書き③

ReactHooksでstateを扱う

Hook(フック)とは

・クラスの機能(stateやライフサイクル)をFunctional Componentでも使えるというもの
・100%後方互換で、古い書き方のコンポーネントにReactHooksを使用したとしても影響を与えない

Hook使用のメリット

シンプルさを保てる為

クラスコンポーネントが複雑(難しい)
1.this.が入ることで他言語と違った挙動で使いづらい為、トレンド的に排除されようとしている
2.stateのロジックが複雑(stateだらけだと意味不明になる)
3.複数のライフサイクルメソッドにまたがる処理をまとめて記述できる

Functional Componentでstateを使ってみる

・ステートフックと呼ばれる
・クラスコンポーネントでの
 this.stateとthis.setstate()の役割
・複数のstateを扱うときはstate毎に宣言する必要がある

useState()の使い方例

1.関数のインポート
import React, {useState} from 'react';
2.宣言
const [state変数名, state変更関数名] = usestate(state初期値);
//実際には↓
const [isHoge, setIsHoge] = useState(false);
3.JSX内で使う
<input /**中略 */ onClick={() => setIsHoge(!isHoge)} />

Functional Componentでstateが使えると何がいいのか?

classコンポーネントを減らす為に上の階層がclassコンポーネントになることが多かった。それによりpropsをどんどん渡していかなければいけなかった為にコードが複雑化しやすかった。
Hookにより末端のコンポーネントでstateを持つことにより管理がしやすくなった。

ReactHooks 関数コンポーネントでライフサイクルを扱う

useEffect()を使用する

メリット

・ライフサイクルの代替が可能
・Functional Componentでライフサイクルを使える(記述が減らせる)
・コードを(classComponentよりも分かりやすく)まとめれる
 →時系列ベースではなく機能ベースでまとめが可能

useEffect()の仕組み

・レンダーごとにuseEffect()内の処理が走るようになる
・メソッドの代替が可能
 compnentDidMount()
 compnentDidUpdate()
 compnentWillUnmount()

基本的な使い方

①レンダーごと

・基本形でuseEffect()内にCallback関数で書く
・Callbackはレンダーごとに呼ばれる
・returnするCallback関数はアンマウント時に呼ばれる
 (クリーンアップ関数とも言う)

useEffect(() => {
  console.log('Render!')
  return () => { //アンマウント時のみ
    console.log('Unmounting!')
  }
})

②マウント時のみ実行させる

・compnentDidMount()の時にだけ書いていた処理を実行するパターン
・第2引数に空の配列を渡すとマウント時(最初の一回)だのみ実行される
 →前回レンダーと今回レンダーで比較している
 (変更があった場合のみCallback関数を実行)

useEffect(() => {
  console.log('Render!')
}, [])

③マウント、アンマウント時に実行する

・①と②の複合系

useEffect(() => {
  console.log('Render!') //マウント時のみ実行される
  return () => { //アンマウント時はクリーンアップ関数として実行される
    console.log('Unmounting!')
  }
}, []) //Updatingの期間は配列が空の為、実行されない

④特定のレンダー時

・useState()と使われることが多い

const [limit, release] = useState(true);

useEffect(() => {
  console.log('Render!') //limitの値を変更(true→false)時に実行される
}, [limit])

いいねボタンの実装

LikeButton.jsx
import React, {useState, useEffect } from 'react';

const LikeButton = () => {
  const [count, counter] = useState(0);
  const [limit, release] = useState(true);

  const countUp = () => {
    counter(count + 1)
  }

  useEffect(() => {
    document.getElementById('counter').addEventListener('click', countUp)
    if (count >= 1000) {
      counter (0)
    }
    return () => {
      document.getElementById('counter').removeEventListener('click', countUp)
    }
  }, [limit]);

  return(
    <>
      <button id={"counter"}> ? {count}</button>
      <button onClick={() => release(!limit)}>もっと!!</button> {/*limitを解除するためのボタン*/}
    </>
  )
}

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

MATERIAL-UIのMultiple SelectでChipから選択を解除する

はじめに

React + Typescript + MaterialUIでフロントエンドを作成していたときに実現したいことができなかったので参考程度に投稿します。

実現できなかった原因

Chipの×ボタンを押してもSelectのメニューが出てきてしまう

実現したいこと

MaterialUIの公式ドキュメントに書かれていた「Multiple Select(Chip)」でChipの×ボタンからも選択を解除したい!
last.gif

結論

Chipコンポーネントに以下プロパティを追加

onMouseDown={(event) => {event.stopPropagation();}}

前提条件

  • typescript,React,MaterialUiについてある程度の知識がある。
  • create-react-appでアプリケーションが作成済み
npx create-react-app {your_app_name} --template typescript
npm install @material-ui/core

Multiple SelectのChipを動かしてみる

まずは何も考えずコードをすべてコピーしてApp.tsxに貼り付ける。
次にChip以外のところは邪魔なのでChipに関連するものだけを残す。
そうするとコードは以下のようになります。

App.tsx
import React from 'react';
import { createStyles, makeStyles, useTheme, Theme } from '@material-ui/core/styles';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    formControl: {
      margin: theme.spacing(1),
      minWidth: 120,
      maxWidth: 300,
    },
    chips: {
      display: 'flex',
      flexWrap: 'wrap',
    },
    chip: {
      margin: 2,
    },
    noLabel: {
      marginTop: theme.spacing(3),
    },
  }),
);

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250,
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name: string, personName: string[], theme: Theme) {
  return {
    fontWeight:
      personName.indexOf(name) === -1
        ? theme.typography.fontWeightRegular
        : theme.typography.fontWeightMedium,
  };
}

export default function MultipleSelect() {
  const classes = useStyles();
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
    setPersonName(event.target.value as string[]);
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-mutiple-chip-label">Chip</InputLabel>
        <Select
          labelId="demo-mutiple-chip-label"
          id="demo-mutiple-chip"
          multiple
          value={personName}
          onChange={handleChange}
          input={<Input id="select-multiple-chip" />}
          renderValue={(selected) => (
            <div className={classes.chips}>
              {(selected as string[]).map((value) => (
                <Chip key={value} label={value} className={classes.chip} />
              ))}
            </div>
          )}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem key={name} value={name} style={getStyles(name, personName, theme)}>
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

そしてこれをnpm startしてhttp://localhost:3000でみると
first.gif
よさそう!※拡大してます

Chipの×ボタンを表示する

Chipに×ボタンを表示するにはChipコンポーネントにonDeleteプロパティを追加すればいい。

Apptsx
<Chip 
key={value} 
label={value} 
className={classes.chip} 
onDelete={() => { alert(value) }} /> #ここを追

今回は×ボタンをがクリックされたことを確認するために、alertでクリックしたChipのラベルを表示するようにした。
そして再度npm start
second.gif

あれ...?何度押しても×ボタンがクリックできずにSelectのメニューが出てきてしまう。

対処法

Chipコンポーネントに以下プロパティを追加

App.tsx
<Chip 
  key={value} 
  label={value} 
  className={classes.chip} 
  onDelete={() => { alert(value) }}
  onMouseDown={(event) => {event.stopPropagation()}}  #ここを追加
/> 

そして再度npm start
third.gif

今度はきちんとクリックされてアラートが出ていますね。

Chip削除の処理を追加する

先ほど入れたalertの処理をChip削除(personName内の名前を削除)する処理に変える。

新しくchipDeleteという関数を用意する

 const chipDelete = (name: string) => {
    setPersonName(personName.filter(value => value !== name))
  }

そうするとApp.tsxfunction MultipleSelect()は以下のようになる

App.tsx
export default function MultipleSelect() {
  const classes = useStyles();
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
    setPersonName(event.target.value as string[]);
  };
  const chipDelete = (name: string) => {
    setPersonName(personName.filter(value => value !== name))
  };

  return (
    <div>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-mutiple-chip-label">Chip</InputLabel>
        <Select
          labelId="demo-mutiple-chip-label"
          id="demo-mutiple-chip"
          multiple
          value={personName}
          onChange={handleChange}
          input={<Input id="select-multiple-chip" />}
          renderValue={(selected) => (
            <div className={classes.chips}>
              {(selected as string[]).map((value) => (
                <Chip
                  key={value} label={value} className={classes.chip}
                  onDelete={(event) => chipDelete(value)}
                  onMouseDown={(event) => { event.stopPropagation(); }}
                />
              ))}
            </div>
          )}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem key={name} value={name} style={getStyles(name, personName, theme)}>
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

そしてnpm start
last.gif

うまくいった!!!!:v:

終わりに

今思うとこんなにすっとできてしまうなんて。。。
ただ単にコピペするだけではなくてどのような処理が行われていて、どんなオプションのプロパティがあるのかを確認しないといけないですね。

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

技術記事を書く時に意識していること

自分のために書く

「思考の整理 + 未来の自分のための資料作りが目的で、周りからの反応はおまけ」
というスタンスでいると雑な記事をバンバン書ける。自分は執筆時間が5分かかっていない記事の方が多いと思う。そして記事数に対してLGTM等は少ない。

自分の言葉で書く

普段自分が使う言い回しをした方が、後から振り返った時に理解しやすいです。カッコつけない方が良い。
文章は、きれいさや構造の正しさより、テンポを意識。

Qiigleの検索に引っかかるようにする

将来検索に使いそうなワードは入れておく
https://qiigle.com/

さっさと本題に入る

「記事の対象者」「この記事でわかること」「この記事で触れないこと」とかが必要なのは超長文記事か、反感を買いかねない踏み込んだ記事を書くときだけで良い。
あいさつとかギャグ的なものは技術記事には必要ない。

その命名じゃないといけないのか、任意の名前で良いのかを明確にする

任意に名前をつけられる部分がアプリ名にちなんだ名前だったりすると、命名規則があるように見えてしまう。多少ダサくなってもわかりやすい方が良い。

参考記事を書く

モラル的な話だけじゃなくて、後から参照する時に執筆当時の状況を思い出しやすくなる。

記事投稿画面をブックマークバーにセット

1クリックで執筆開始できます。

伸ばしたい記事は拡散の努力をする

Qiitaではトレンドに入ると一気に伸びるので、サービス公開記事など伸ばしたい記事はTwitterやオンラインコミュニティ等でコメント付きで共有するなど、最大限拡散の努力をします。やりまくると嫌われるので、本当に伸ばしたい時だけ。

記事を伸ばすために学習するジャンルを変えない

経験上、Railsの記事を書くよりReact等の記事を書く方が全然伸びます。でもそれはそれなので、、
「記事が伸びる分野 = 自分が人柱になる確率が高い分野」で、今の自分は確実に技術力の土台を固めるフェーズなので、そこは履き違えないように注意してます。

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

超初心者のReact(create-react-app)基礎学習覚え書き②

項目

・ライフサイクルメソッド
・importとexportモジュールの使用

ライフサイクルメソッド

3種(Mounting,Updating,Unmounting)
・それぞれコンポーネントの配置、成長、破棄を示す
・それぞれの期間で使えるメソッドがいくつか存在する

そもそもなんでつかうの?

関数の外(Reactコンポーネントの外)に影響を与える関数を記述する場合に使用するため

ライフサイクル内で行う一般的項目として
バーチャルDOMの内容変更、データベース通信のようなAPI通信の際、ログ出力、setState()など

どういったものかは下記の通り

配置(WebページへのMount)

Webページにアクセスした時にReactで作ったコンポーネントが配置(生まれる)までの期間

変更(Update)

特定の箇所をクリックすると色が変わる(ユーザーの操作)、データが書き換わることでUIが変更されるなどが発生する期間

破棄(Unmount)

現在ページから別ページへのアクセスをした時
もとのページのコンポーネントは不要になるためコンポーネントを破棄する期間
?期間というかタイミング?

主要なメソッドと時間軸ごとの動き方

Mounting→ Updating→ Unmounting→
constructor()
初期化(state)
render()
VDOMを描画
(JSXをリターン)
render()
VDOMを描画
conponentDidMount()
render()の後に1度だけ呼ばれる
リスナーの設定やAPI通信に使用
- conponentDidUpdate()再レンダー後に呼ばれるイベント
- - componentWillUnmount()破棄される直前に呼ばれるもの
リソース解放、リスナー解除他…

実装例 ?buttonの実装

※コンポーネント中の関数名はファイル名で合わせていく
Hoge.jsが主要メソッド使用の例

Foo.jsx
import React from 'react';
import Bar from "./Bar";

const Article = (props) => {
  return (
    <div>
      <h2>{props.title}</h2>
      <LikeButton count={props.count}/>
    </div>
  )
};
Bar.jsx
import React from 'react';

const LikeButton = (props) => {
  return(
    <button id={"counter"}> ?  {props.count}</button>
  )
}

export default Bar

Hoge.jsx
import React from 'react';
import Foo from "./Foo";

class Hoge extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

//componentDidMount
  componentDidMount() {
    //ボタンがクリックされたらGoodをカウントアップする
    document.getElementById('counter').addEventListener('click',this.countUp)
  }

//componentDidUpdate
  componentDidUpdate() {
    if (this.state.count >= 100) { //100になったら
      this.setState({count:0}) //0に戻る
    }
  }
//componentWillUnmount
  componentWillUnmount() {
    //無駄なリソースの解放によサーバー負荷が軽減される
    document.getElementById('counter').removeEventListener('click',this.countUp) //countUpのリスナーをremoveする
  }

  countUp = () => { //関数が呼び出されたら
    this.setState({count: this.state.count +1 }) //stateを変更します(現在のcountに+1します)
  }

  render() {
    return (
      <>
        <Article 
          title={"ライフサイクルメソッドの使い方"}
          count={this.state.count}
        />
      </>
    )
  }
}

export default Hoge

importとexportモジュールの使用

Reactで使用するモジュールの概念をうまく使うために必須項目

モジュール化

モジュール・・・?

→大きなものをいくつかに分割して管理していく方法のこと
→基本1ファイル1モジュールとする
→任意の場所で読み込んで使用できる

メリット

1.大規模アプリケーションになっても管理しやすい
2.任意の場所で読み込み、かつ最小限で済む為読み込み速度にも影響する
3.プログラムの使いまわし(再利用)が可能な為、何度も同じコードを書く必要が無い

1つのコンポーネントを作成したら、ひとつのモジュールになっているので必要なところで読み込む
また、コンポーネントをいくつかに分けることにより管理しやすくしている

export

名前付きexport

・1モジュールから複数の関数をexportできる
 ※クラスはexportできない

FooBar.js
export function Foo() {
  return (<h1>Fooo</h1>)
}//通常関数
export const Bar = () => {
  return (<h1>Baar</h1>)
}//アロー関数

名前無しexport(default)

※ES6推奨の書き方
・1ファイル(1モジュール)1export
・アロー関数は宣言後にexportする
・クラスをexportできる

Foo.js
export default function Foo() {
  return (<h1>Fooo</h1>)
}
Bar.js
const Bar = () => {
  return (<h1>Baar</h1>)
}
export default Bar
Hoge.js
export default class Hoge exteds Fuga {
  render() {
    return (<h1>Hoge</h1>)
  }
}

モジュールのインポート

方法は3種類

1.全体のインポート
2.一部(関数ごと)だけのインポート
3.別名でのインポート

1.全体のインポート

・名前無し(default)exportしたモジュールをimportする
・モジュール全体をimport

Foo.js
import React from 'react'; //npmで管理しているreactというパッケージの中のReactというモジュールをimportしているよの意味
import Bar from "./Bar"; //BarというファイルからBarというモジュールをimportしているよという意味
Bar.js
const Bar = (props) => { //アロー関数でBarコンポーネントを宣言
  return (<div>Barです</div>)
};
export default Bar //コンポーネントのexport

2.一部(関数ごと)だけのインポート

・名前付きexportされたモジュールのimport
・{}内にimportしたい関数名

Hoge.js
import { Foo, Bar } from "./FooBar"; //FooBarというファイルからFooとBarをimportするという書き方になる
FooBar.js
export function Foo() {
  return (<h1>Foooo</h1>)
}
export const Bar = () => {
  return (<h1>Baaar</h1>)
}
//1ファイルだけど2つのモジュールで構成される

3.別名でのインポート

・別名(エイリアスとも言う)をつけてimportできる
→モジュール全体の場合:* as name
→モジュール一部の場合:A as B

HogeHoge.js
import React from 'react';
//↓モジュール全体の場合:* as name
import * as FooBar from './FooBar' //FooBarって名前で./conponents/FooBarに保管のモジュールをimportしますという書き方
//↓モジュール一部の場合:A as B
import { Foo as MyFoo } from './FooBar' //FooBarというファイルにあるFooという関数をMyFooという名前でimportするという書き方


  render() {
    return (
      <>
        <FooBar.Foo /> //FooBarの中のFoo関数
        <FooBar.Bar /> //FooBarの中のBar関数
        <Hoge />
      </>
    )
  }
}

export default HogeHoge

次回③はReactHooks state

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

【React・ReactNative】任意の場所まで自動スクロールさせる方法

はじめに

任意の場所まで自動スクロールするには、標準のWebAPIで提供されているElement.scrollIntoView()が便利です。
チャットアプリで送受信に合わせて最新のメッセージまでスクロールしたい時などに使えます。

DOMのElementはReactであればReact.useRef()で取得することができます。

実装の例

任意の場所にスクロールしたい場合、あらかじめその場所に透明な<div/>を仕込んでおくと便利です。

  • 透明な<div/>を長いリストの最後に仕込んでおく
  • React.useRef()透明な<div/>の参照を取得
  • 透明な<div/>の参照に対してElement.scrollIntoView()`を使用する
関数コンポーネント
const messageEndRef = React.useRef();

const scrollToLatest = (behavior = 'smooth') => messageEndRef.current.scrollIntoView({ behavior });

return (
<List>
  {renderMessages()}
  <div
    style={{ float:"left", clear: "both" }}
    ref={messagesEndRef}
  />
</List>

まとめ

  • 標準のWebAPIで提供されているElement.scrollIntoView()を使うと簡単に自動スクロールできます。
  • Element.scrollIntoView()を使う対象として、透明な<div/>を使う方法があります。

参考

https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView

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

React(フロント)からHasuraのgraphqlを叩く

前回hasuraを使ってgraphqlサーバを立てたので、今回はフロントと繋げたいと思います。
繋げるために利用したライブラリはapolloです。

ドキュメント

https://www.apollographql.com/docs/tutorial/client/

1. npm install

まずはライブラリをインストールしましょう。

npm install @apollo/client

2. ApolloProviderでアプリをラップ(アプリのどこでも利用できるように)

次に、ApolloProviderコンポーネントでアプリをラップし、ApolloClientインスタンスを渡しましょう。

index.jsx
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
import React from "react";
import ReactDOM from "react-dom";
import Pages from "./pages";
import injectStyles from "./styles";

const client = new ApolloClient({
  uri: "http://localhost:4000/",
  cache: new InMemoryCache()
});

injectStyles();
ReactDOM.render(
  <ApolloProvider client={client}>
    <Pages />
  </ApolloProvider>,
  document.getElementById("root")
);

3.クエリを投げる

準備はできました。
以下のようにクエリを投げれば結果を確認することができます。

sample.jsx
import { gql, useQuery } from "@apollo/client"; 

const GET_SAMPLE = gql`
    query getSample {
      sample {
        id
        message
      }
    }
  `;

  const { data, loading, error } = useQuery(GET_SAMPLE);
  console.log(data); //データの取得が確認できる

注意

gqlの中身ですが、Hasura上でクエリを投げる分には以下のみでよかったと思います。

{
   sample {
        id
        message
   }
}

しかし、フロントから投げる時は「query "クエリ名"(キャッシュ用)」をつける必要があります。

query getSample {
      sample {
        id
        message
      }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無料でSSR・ホスティング・API鯖を立てれるVercel。無料でホスティングするサンプル。Parcel・ReactでSPA。

Vercel
https://vercel.com

バックナンバー
無料でSSR・ホスティング・API鯖を立てれるVercel。TypeScript・ExpressでAPI鯖を立てる。

ソースコード

package.json
{
  "scripts": {
    "start": "rimraf local && parcel src/index.html -d local",
    "build": "rimraf public && parcel build src/index.html -d public --no-source-maps"
  },
  "dependencies": {
    "connected-react-router": "^6.8.0",
    "history": "^5.0.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-redux": "^7.2.1",
    "react-router": "^5.2.0",
    "redux": "^4.0.5",
    "vercel": "^20.1.0"
  },
  "devDependencies": {
    "parcel-bundler": "^1.12.4",
    "rimraf": "^3.0.2"
  }
}
src/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vercelホスティング</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/index.js"></script>
  </body>
</html>
src/index.js
import * as React from 'react';
import { render } from 'react-dom';
import { combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { Route, Switch } from 'react-router';
import {
  ConnectedRouter,
  routerMiddleware,
  connectRouter,
} from 'connected-react-router';
import { applyMiddleware, compose, createStore } from 'redux';
import { createBrowserHistory } from 'history';

const App = () => {
  return (
    <>
      <h1>Hello world!</h1>
    </>
  );
};

const history = createBrowserHistory();

const store = createStore(
  combineReducers({
    router: connectRouter(history),
  }),
  {},
  compose(applyMiddleware(routerMiddleware(history)))
);

render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Switch>
        <Route exact path={'/'} component={App} key="home" />
        <Route exact path={'/sub'} component={App} key="sub" />
      </Switch>
    </ConnectedRouter>
  </Provider>,
  document.getElementById('app')
);
vercel.json
{
  "routes": [
    { "handle": "filesystem" },
    {
      "src": "/.*",
      "dest": "/index.html"
    }
  ]
}

コマンド

ローカルで実行 ポート番号3000で鯖が立ち上がる

$ npx vercel dev

デプロイ

$ npx vercel --prod

ワイの成果物

https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378

①コード共有サイト(SPA) React
https://code.itsumen.com

②画像共有サイト(SPA) React
https://gazou.itsumen.com

③チャット(SSR) Nuxt.js
https://nuxtchat.itsumen.com

④チャット(SPA) React
https://chat4.itsumen.com

⑤掲示板(SSR) Next.js
https://board.itsumen.com

⑥掲示板(SPA) Vue
https://board.itsumen.com

⑦レジの店員を呼ぶスマホアプリ(Android)
https://play.google.com/store/apps/details?id=com.itsumen.regi&hl=ja

⑧ブログ(静的サイトジェネレータ) Hugo
https://yuzuru.itsumen.com

ワイのLINE: https://line.me/ti/p/-GXpQkyXAm

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

gatsby入門 チュートリアルをこなす ソースプラグインの作成(1)

チュートリアルをこなす!

以前にgatsbyの基本のチュートリアルをこなしたのですが、まだチュートリアルが残っているので最後までやっていこうと思いました。
今回実施するgatsbyのチュートリアルはこちら
https://www.gatsbyjs.com/tutorial/plugin-and-theme-tutorials/
https://www.gatsbyjs.com/tutorial/source-plugin-tutorial/
早速やっていきましょう。

Plugin & Theme Tutorials

https://www.gatsbyjs.com/tutorial/plugin-and-theme-tutorials/
ここではプラグインとテーマのチュートリアルの概略が記載されていました。
ざっくり言うと
プラグインは、Gatsby APIを実装するNode.jsパッケージ。
テーマは、事前設定された機能やデータソーシング、UIコードをギャツビーサイトに追加するプラグインの一種。
要はサイト構築に便利な物が作れまっせ(しかも共有出来まっせ)ってことで理解しました。
次行きましょう。

Creating a Source Plugin

https://www.gatsbyjs.com/tutorial/source-plugin-tutorial/
ここでは独自のソースプラグインを作成するようです。
ソースプラグインについてはチュートリアルにこう書かれています。

ソースプラグインは、任意のソースからのデータをGatsbyが処理できる形式に変換します。 Gatsbyサイトでは、いくつかのソースプラグインを使用して、興味深い方法でデータを組み合わせることができます。

つまり構築サイト内のソースからデータを抜き出して、良い感じのデータに変換できるってことかな?
とにかく次次!

How to create a source plugin

なんだか色々な事を書いてあるけど英語よくわかんねぇから、とりあえず実技に進もう。

Set up an example site

以下コマンドでサンプルサイトを作成
gatsby new example-site https://github.com/gatsbyjs/gatsby-starter-hello-world
2020-09-09_01h30_27.jpg

Set up a source plugin

以下コマンドでソースプラグインを作成
gatsby new source-plugin https://github.com/gatsbyjs/gatsby-starter-plugin
2020-09-09_01h54_05.jpg

Install your plugin in the example site

サンプルサイトにソースプラグインをインストールします。
example-site/gatsby-config.jsを以下のように修正します。

example-site/gatsby-config.js
module.exports = {
  /* Your site config here */
  plugins: [require.resolve(`../source-plugin`)],←ここ修正
}

example-siteを起動します。example-siteディレクトリに移動して以下を実行
gatsby develop
2020-09-09_02h03_06.jpg
ロードされてる!
このログはsource-plugin/gatsby-node.jsに出力コマンドがあります。

source-plugin/gatsby-node.js
exports.onPreInit = () => console.log("Loaded gatsby-starter-plugin")

Source data and create nodes

source-plugin/gatsby-node.jsを以下のように書き換え

source-plugin/gatsby-node.js
// constants for your GraphQL Post and Author types
const POST_NODE_TYPE = `Post`

exports.sourceNodes = async ({
  actions,
  createContentDigest,
  createNodeId,
  getNodesByType,
}) => {
  const { createNode } = actions

  const data = {
    posts: [
      { id: 1, description: `Hello world!` },
      { id: 2, description: `Second post!` },
    ],
  }

  // loop through data and create Gatsby nodes
  data.posts.forEach(post =>
    createNode({
      ...post,
      id: createNodeId(`${POST_NODE_TYPE}-${post.id}`),
      parent: null,
      children: [],
      internal: {
        type: POST_NODE_TYPE,
        content: JSON.stringify(post),
        contentDigest: createContentDigest(post),
      },
    })
  )

  return
}

再起動
graphqlを見ると
2020-09-09_02h17_38.jpg
allPostという項目が増えています。
チュートリアル通りのクエリを実行するとこんな感じ。
うん。source-plugin/gatsby-node.jsに書かれたPost情報が記載されてる。
postもあるね。
なるほどね。サイトのディレクトリとファイルの内容だけでSQLみたいなクエリ作って取得するようにイメージしておこう。

だめだ超眠い。

今回はここまで。

ありがとうございました。

gatsby 過去の作業履歴

gatsby入門 チュートリアルをこなす 0.開発環境をセットアップする
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(1)
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(2)
gatsby入門 チュートリアルをこなす 2. ギャツビーのスタイリングの概要
gatsby入門 チュートリアルをこなす 3. ネストされたレイアウトコンポーネントの作成
gatsby入門 チュートリアルをこなす 4. ギャツビーのデータ
gatsby入門 チュートリアルをこなす 5. ソースプラグインとクエリされたデータのレンダリング
gatsby入門 チュートリアルをこなす 6. 変圧器プラグイン※Transformer pluginsのgoogle翻訳
gatsby入門 チュートリアルをこなす 7. プログラムでデータからページを作成する
gatsby入門 チュートリアルをこなす 8. 公開するサイトの準備
gatsby入門 ブログ作ってサーバーにアップしてみる

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