20200319のReactに関する記事は7件です。

Reactは怖くない!入門1

はじめに

Reactが怖い、書き方全くわかんない!!って人がReactに抵抗がなくなることを目的としてこの記事書いてます。
めちゃくちゃ噛み砕いてるつもりです。
間違った表現や、もっと説明するべきものがあれば教えてください。
初心者の方は、ガンガン質問してもらえると嬉しいです。

Reactって何

javascriptのライブラリ
UIを部品と考えその部品をたくさん作り、それを組み合わせて画面を作ることができます。

ディレクトリ構造(例)

src/
    components/
        Hoge.js
    index.js
    App.js
public/
    index.html
package.json
webpack.config.js

各ディレクトリの説明

src/

reactのcomponentというか実装はこの中に全部書いていく

public/

htmlファイル、画像ファイルはここに配置する。
webpackでビルドされたファイルの出力先もここになることが多い

package.json

使用するライブラリとか、コマンドの設定をするところ

webpack.config.js

複数のjs, image, cssを1枚のjsファイルにいい感じにまとめてくれるもの。(設定次第で分割とかできるけども)
src/*を読み込んで、public/に出力って設定したりできます

その1枚にまとめられた ファイルをhtmlで読み込むことによってReactが動いてくれる

軽くReactComponentをどうやって表示させてるかについて

一番元となるファイル

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

こいつ ReactDOM.render(<App />, document.getElementById('root'))
AppというReactのcomponentをidがrootのdomに描画してくれる。
Appの内部で変更が起これば再描画してくれる。
基本的にはあまりここをつつくことがないので最初のうちは気にする必要はないかも。

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="/js/bundle.js"></script> //ここでwebpackで出力した1枚のjsを読み込む
  </body>
</html>

このhtmlを表示すると<div id="root"></div> の中にReactのcomponentが表示されている

前提知識

今の段階ではなんのことかはわからないかもしれないが、頭の隅に置いておいてください。

stateとprops

State: そのコンポーネントが持っている状態・イミュータブル
Props: 親コンポーネントから渡されたプロパティ・ミュータブル

サンプル
const Parent = () => {
    const [hoge setHoge] = useState(''); //これはstate

    return <Child hoge={state} />
}

const Child = ({ hoge }) => ( //ここのhogeはpropsと呼ばれる
    <div>{hoge}</div>
)

stateを変更する場合は 特別な関数を通して置き換えます。ここでいうとsetHogeを通してstateを変更しなければいけません

アロー関数

componentを作る時、関数を定義するときは基本的にallow関数を使います。

下の二つの関数は同じ意味です。

function hoge(params) {
  return params
}

const hoge = params => params

関数の中でいろいろ処理を書かず、returnだけで済まされる場合はreturnを省略できます。
また、引数が一つの時は ()を省略できます。

省略できないパターン
const hoge = (hage1, hage2 ) => {
    const sum = hage1 + hage2

    return sum
}

(まあhage1 + hage2を変数に置く必要ないんですが、一応例として)

あとthisを気にしなくてよくなるとかあるのですが、最初のうちはあまりわかってなくてもいいです。
https://qiita.com/sitilma/items/5c2a4ed03d53896321a6

stateの扱い

基本的に、stateは上位componentだけで定義します。
そして下のcomponentにバケツリレーで渡して使っていきます。
適当にどこでもstateを定義してしまうと意味がわかんなくなっちゃいます。
(例外として下で定義することもありますが今は気にしないでください)

//定義
const [state, setState] = useState(''); // state = '';
const [hoge, setHoge] = useState(''); // hoge = '';

//変更する時
setState('page') // state = page;
setHoge('ooooooooo') // hoge =  ooooooooo;

// やっちゃダメな奴
state = 'page';
hoge = 'ooooooooo'

ちゃんとset〇〇のような関数を通して変更しましょう。

実践です

簡単なToDoList作りましょう。
画面収録-2020-03-19-17.22.35.gif

仕様
・textfieldに打った文字がリアルタイムで下に表示される
・登録ボタン押すとそれが保存される
・クリアボタンを押すとリセットされる

npm install -g create-react-app
create-react-app [プロジェクト名]
このコマンドでreactの雛形作ることができます。

最初は意味がわからないと思うので、サンプルコードを参考に書いてもらうことがいいかもしれません。
参考にしてください。
https://github.com/Tsukuni/react-sample-app

終わりに

入門2も書くのでお待ちください。

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

Reactのメインコンセプト要点まとめ(1)

この記事の位置づけ

React公式ドキュメントの「メインコンセプト」について、実際にReactの開発を行う中で特に参考になった章を要点の解説付きで纏めたものである。実際にReact公式ドキュメントを読み解いてもらうのが一番だが、時間がない人に要点だけでも読んでもらえるようにしたいと思い、纏めている。

公式のメインコンセプトでは、以下の章立てで説明をしている。
1. Hello World
2. JSXの導入
3. 要素のレンダー
4. コンポーネントとprops
5. stateとライフサイクル
6. イベント処理
7. 条件付きレンダー
8. リストとkey
9. フォーム
10. stateのリフトアップ
11. コンポジションvs継承
12. Reactの流儀

当記事では、以下の3章分について要点まとめをしている。
1. Hello World
2. JSXの導入
3. 要素のレンダー

出典元資料

React公式ドキュメントのメインコンセプト

メインコンセプト

1. Hello World

一言コメント

まずは実践的なチュートリアルを使って、手を動かしてReactの動作を学習するのが望ましい。
まだチュートリアルをやっていない場合は、React公式チュートリアルを実際に作ってみて、感触を掴むことをお勧めする。

また、Reactは最新のJavaScript標準であるEcmaScript6でコードを記述することになる。
これまでJavaScriptでコードを書いたことがなかったり、最近のClassやアロー演算子といった書き方にまだ触れたことがない人は、まずはEcmaScript6基準のJavaScriptについて学習することをお勧めする。

2. JSXの導入

知っておくべきポイント

JSXを使う理由

UIがどのような見た目なのかを記述するために、ReactとともにJSXを使用することを公式は推奨している。
ReactでJSXを使うことは必須ではないが、JSXがあるためにReactは有用なエラーや警告をより多く表示できる。

hello.js
const element = <h1>Hello, world!</h1>;

JSXに式を埋め込む

以下のように中括弧を使用することで、JSX内に式を埋め込むことができる。

name.js
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

JSXで子要素を指定する

以下のように、JSX内で子要素を指定することで複数の要素を含め入れることも可能。

hello.js
const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

また、上記の書き方だとDIVタグが無駄に増えるという問題があるので、以下のような書き方もサポートされている。

hello.js
const element = (
  <>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </>
);

一言コメント

最初はキモいが、慣れるとJSXでUIを構築するのが当たり前になる。便利。

3. 要素のレンダー

知っておくべきポイント

Reactの「要素」とは何のことなのか

要素とはReactアプリケーションの最小単位の構成ブロックです。

ふむ。。。
もう少し噛み砕くと、以下のようにReactのUIを定義したオブジェクトはReact要素であると言える。

hello.js
const element = <h1>Hello, world</h1>;

公式でも「要素とコンポーネントを混同する人がいる」と指摘されているが、Reactの「要素」と「コンポーネント」は別の意味を持つ言葉である。公式では「要素とはコンポーネントを構成するもの」とされている。(要素はコンポーネントに内包されているUI定義)

ReactDOM.render() と React.Component.render() の違いは何なのか?

ReactDOM(公式ドキュメント)
React.Component(公式ドキュメント)

違いとしては、、、

  • ReactDOM.render(element, container)は、containerとして渡されたDOMに、React要素を実際にレンダリングしている
  • React.Component.render()は、React.Componentthis.propsthis.stateを調べた上でReact要素を返す(DOMノードのレンダリングはこの時点では行っていない)

ざっくりまとめると、以下のような使い分けになると思っている。

  • コンポーネント単位(UIの部品単位)のReact要素を定義するのはReact.Component.render()で行う
  • HTMLのどのDOM要素にReactを描画するか指定するのはReactDOM.render()を使用する
    • Create React Appで例えば、index.htmlの<div id="root"></div>にReact要素を描画している
    • 上記で指定したDOM要素にReact要素(Appコンポーネント)を描画するために、index.jsではReactDOM.render(<App />, document.getElementById("root"))と記述している
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <!--
      省略
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      省略
    -->
  </body>
</html>
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

レンダリングされたReact要素の更新

React要素はイミュータブルであり、一度React要素を作成すると変更することはできない。
イミュータブルということは、「変更」はできないが「置き換え」は可能ということである。
よって、ReactでUIを更新する唯一の方法は、新しいReact要素を作成してReactDOM.render()に渡してDOMを再描画することになる。

ただし、実際には大抵のReactアプリケーション(Create React App含め)では、ReactDOM.render()は一度しか呼び出さない。
それらのReactアプリケーションでは、ReactDOM.render(element, container)のelementで渡すことになるReact.Componentにおいて、React.Component.render()関数を使用してコンポーネント単位のUI部品を置き換えることになる。

一言コメント

チュートリアルをやっただけでは、ReactDOM.render()React.Component.render()の違いはイメージがつきにくいかもしれない。
ただ、DOMツリーとReactの紐付けをしているのはReactDOMであり、ここはぜひ抑えておいたほうがよいと思う。

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

Material UIのスタイル指定(JSS)

Material UIでのスタイル指定方法を説明します。Material UIのスタイルシステムの実体はJSSです。themeについては言及しません。もっと基礎的なところだけです。

classNameで即値指定

HTMLで言うclass=の指定。Material UI的には何もしない。BEMやscssでもなんでもいいのですが、外部スタイルシートを定義してあてがっていくパターン。

<Hoge className="hoge fuga"/>

styleで指定

styleプロパティの指定。Material UI的には何もしない。オブジェクトがdomのstyle属性に変換されて付けられる、Reactの仕組み。

<Hoge style={{fontSize:1, padding: '1rem'} />

DOM上は、要素に直接紐付いたスタイル指定(devtool上、element.styleで表示される)となる。疑似要素は原理的に指定不可。

StyledComponent?で指定。

こちらを参照。本資料では説明を割愛する。

JSSのスタイルシートオブジェクトで指定

Material UIがかかわるのはここから。JSSは基本的にDOMのインラインスタイル属性ではなく、クラス名による指定の仕組みである。

大きな流れ

以下のような流れとなる。

  1. スタイルシートの元になる「元のスタイルシートオブジェクト」をJSで作る const styles = { root: { backgroundColor: 'red', }, };
  2. 「元のスタイルシートオブジェクト」を元に、Material UIが準備しているHoCやhooksを使って、以下を生成する。
    • 「元のスタイルシートオブジェクト」のプロパティ名と、コンポーネントローカルにリネームされた実際に生成される「クラス名の対応表」。こんなやつ: {"tabBar":"TabBar-tabBar-284","selectedTab":"TabBar-selectedTab-285"}
      この値自体をプログラマが意識することは(デバッグ時を除き)基本的にはない。
    • この実クラス名でのスタイル定義が、背後で<head>に挿入される(プログラマは意識しないでよい)。
  3. 「クラス名の対応表」をMaterial UIが提供するReactコンポーネントにおいて以下のいずれかの方法で利用する
    • 「クラス名の対応表」から実クラス名を取得して「classNames」propsで指定する。
    • 「クラス名の対応表」から実クラス名を「classes」propsで指定する。

「元のスタイルシートオブジェクト」の作りかた

単なるJSオブジェクトとして「スタイルシートオブジェクト」を作る

const styles = {
  root: {
    backgroundColor: 'red',
  },
};

もしくは

const styles = createStyles({
  root: {
    backgroundColor: 'red',
  },
});

両者の意味は同じである。後者ではTypeScriptの型エラーを黙らすことができる。

themeを引数とする関数として「スタイルシートオブジェクト」を作る

const styles = (theme: Theme) =>{
  root: {
    backgroundColor: theme.color.red,
  },
};

もしくは

const styles = (theme: Theme) => createStyles({
  root: {
    backgroundColor: theme.color.red,
  },
});

両者の意味は同じである。後者ではTypeScriptの型エラーを黙らすことができる。

ここでは、Reactのインラインstyle属性のようにJavaScriptオブジェクトを用いるが、rootの階層が入っていることが異なる。この階層の名前は、(実CSSクラス名を隠蔽した)、ローカルなCSSクラス名と思ってよい。

CSSクラスなので疑似クラスも指定できる。たとえば、以下のように:hover疑似クラスが指定できる。(JSSの機能)

const styles: any = (theme: Theme): StyleRules =>
  createStyles({
    button: {
      margin: '0.3rem',
      '&:hover': { transform: 'scale(1.1)' },
    },

「元のスタイルシートオブジェクト」から「クラス名の対応表」を入手する

React Hooks系で

こちらから引用だが、以下のように「makeStyles」の引数に「元のスタイルシートオブジェクト」を渡すと、「クラス名の対応表」を取得できるフック関数を入手することができる。そのフック関数の呼び出し結果をclasses変数に取得している。(createStylesは使用していない。)

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles({
  root: {
    backgroundColor: 'red',
    color: props => props.color,
  },
});

export default function MyComponent(props) {
  const classes = useStyles(props);
  return <div className={classes.root} />;
}

HoC系で

HoCであるwithStylesを使う。こちらも引用だが以下となる。意味はHooks系と同じだが、「元のスタイルシートオブジェクト」をwithStylesの入力として与え、「クラス名の対応表」がprops.classesに得られるようなコンポーネントを生成している。

import React from 'react';
import { withStyles } from '@material-ui/core/styles';

const styles = {
  root: {
    backgroundColor: 'red',
  },
};

function MyComponent(props) {
  return <div className={props.classes.root} />;
}

export default withStyles(styles)(MyComponent);

取得した「クラス名の対応表」の使いかた

「クラス名の対応表」は、入手方法としてHooksでもHoCで得ることができるが、同じものである。上記サンプルでは、入手した「クラス名の対応表」はclassesという名前のprops(props.classes)もしくは変数classesに格納している。

「クラス名の対応表」には2つの使いかたがある。

classNameに与える

サンプルにもあるが、

  return <div className={classes.root} />;

のように指定する。複数あれば

  return <div className={classNames(classes.root,classes.hoge)} />;

のようにNPMモジュールclassnamesを使うのがよいだろう。classnamesはクラス名を結合させているだけである。(clsxというのもあり効率がよいらしい)。

classesで指定

最後になったが、これがJSSの本領である。Material UIのコンポーネントにはCSS APIというものが定義されており、それを使ってカスタマイズすることができる。
たとえば、Tabsコンポーネントには以下のようにかかれている

CSS
- Style sheet name: MuiTab.
- Style sheet details:

Rule name Global class Description
root .MuiTab-root Styles applied to the root element.
labelIcon .MuiTab-labelIcon Styles applied to the root element if both icon and label are provided.
textColorInherit .MuiTab-textColorInherit Styles applied to the root element if the parent Tabs has textColor="inherit".
textColorPrimary .MuiTab-textColorPrimary Styles applied to the root element if the parent Tabs has textColor="primary".
textColorSecondary .MuiTab-textColorSecondary Styles applied to the root element if the parent Tabs has textColor="secondary".
selected .Mui-selected Pseudo-class applied to the root element if selected={true} (controlled by the Tabs component).

これらを必要に応じてオーバーライド定義することができる。赤字"selected"は後述の説明で例として使用するCSS APIのキー名である。このスタイルをオーバーライドする方法は3つある。

  • With a rule name of the classes object prop.(classes propsのルール名)
  • With a global class name. (グローバルクラス名)
  • With a theme and an overrides property..(テーマと上書きプロパティ)

以降では最初の「 rule name of the classes object prop.」について説明する。
やるべきことは、classes属性に、指定したCSS API名をキー(ここではslected)とし「クラス名の対応表」の実クラス名を指定するのである。

const styles = (theme: any) =>
  createStyles({
    selectedTab: {
      backgroundColor: '#eeeeff',
      fontWeight: 'bold',
    },
  });
:
          <Tabs>
            <Tab
              classes={{ selected: classes.selectedTab }} />
               :
          </Tabs>

するともともとMUIで定義されていたCSSに上書きされる形でCSSスタイルが定義される。

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

Reactで外部ドメイン向けにリンクを生成する(ReactRouterだとできない?ので。。)

概要

Reactで外部ドメイン向けにリンクを生成する(ReactRouterだとできない?ので。。)

実装

↓で定義したLinkToOtherDomainコンポーネントを使う。

import React from "react";

export const LinkToOtherDomain = props => {

    const { to, children, ...other } = props;
    return (
        <a href={to} {...other} rel="noopener noreferrer">
            {children}
        </a>
    );
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

create-react-app の オプション --typescript は古い

v2.1.0のリリースノートでは確かに

$ npx create-react-app my-app --typescript

とあるが、そこからリンクされているドキュメントの最新版(現時点でv3.4.0)にかかれているコマンドは

npx create-react-app my-app --template typescript
# or
yarn create react-app my-app --template typescript

だ。
未だに --typescript なんて説明しているドキュメントは投げ捨てるべし。
(記述が古いか1次資料を参照していない)

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

WebpackでCSSの読み込み順番を変えたくてつまづいた件と一時的な解決方法について

こんばんは。

今回は、React + Material-UIで、CSS Modulesを使っていた際に、自分で書いたCSSが反映されなかったときのことと、一時的な対処方法についてです。

調べても全く情報が出てこなかったため、記事にしました。

間違っている点や、よりよい方法があれば教えて下さい!

CSSが反映されていないときの状態

Material-UIでつくったボタンに、オリジナルのスタイルを効かせようとして、

SignupButton.tsx
import * as React from 'react'
import Button from '@material-ui/core/Button'
import styles from './SignupButton.module.css'

const SignupButton: React.FC = () => {
  return (
    <Button className={styles.signup}>新規登録</Button>
  )
}

export default SignupButton
SignupButton.module.css
.signup {
  background-color: #aed581;
}

.signup:hover {
  background-color: #8bc34a;
}

あと、TypeScriptだからCSSの型情報も書いて、

SignupButton.module.css.d.ts
declare const styles: {
  readonly "signup": string;
};
export = styles;

さあ、ブラウザで確認!

スクリーンショット 2020-03-19 3.17.25.png

ぴえん

CSSが反映されていません。

これが、起こった内容です。

原因の追求と解決

いろいろ探しているとスタイルの読み込み順番が問題であることが判明。

スクリーンショット 2020-03-19 3.19.14.png

ネットで同じ問題を抱えていた人がいないか探してみるも、何も出てこず。

なんとなく、Webpackでバンドルしたときが原因であることがわかっていたので、

調べてみると、style-loaderドキュメントにこんなものが、

Function
Allows to override default behavior and insert styles at any position.

Google先生に翻訳をお願いすると、

Function
デフォルトの動作をオーバーライドし、任意の位置にスタイルを挿入できます。

これを書き換えれば、スタイルの読み込み順番を変更できそう!

Webpackの設定ファイルを開いて書き換えてみた、

webpack.config.js
...
      {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader',
            options: {
              insert: function insertAtBottom(element) {
                var parent = document.querySelector('body');
                parent.insertBefore(element, parent.firstChild);
              }
            }
          },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
...

これで、bodyの一番上にオリジナルのスタイルが挿入される!

ブラウザで確認してみると、

スクリーンショット 2020-03-19 3.38.55.png

スクリーンショット 2020-03-19 3.41.28.png

かちー

なんとかスタイルを効かせることができた...

おわりに

本当は、オリジナルのスタイルをheadの一番下に入れたかったが、うまくできませんでした。

今後、WebpackとMaterial-UIを詳しく調査して、オリジナルのスタイルをheadの下に入れれるように修正したいと考えてます。

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

【React】Warning: Cannot update a component from inside the function body of a different component.

Reactのバージョンを16.12.0 → 16.13.0にした時に下の様なWarningが出て困った。

Warning: Cannot update a component from inside the function body of a different component.

なんだこのWarning!?

日本語の情報が少ない中、reactのissuesやstackoverflowを一生懸命探してみたが、最終的に公式のBlogにたどり着いた。
https://reactjs.org/blog/2020/02/26/react-v16.13.0.html

It is supported to call setState during render, but only for the same component. If you call setState during a render on a different component, you will now see a warning:

レンダリング中にsetStateを呼び出すことはサポートされていますが、同じコンポーネントに対してのみです。別のコンポーネントでのレンダリング中にsetStateを呼び出すと、警告が表示されます。

ということらしい。

これだとWarningが出る。
Hogeのレンダリング中にFugaでsetStateを呼び出しているのが原因っぽい。

App.tsx
import React, { useState } from "react";
interface Props {
  setCount: Function;
}
const Fuge: React.FC<Props> = ({ setCount }) => {
  setCount(1);
  return <p>test</p>;
};
const Hoge: React.FC = () => {
  const [count, setCount] = useState(0);
  return <Fuge setCount={setCount} />;
};
export default Hoge;

これを解決する方法は、もちろん公式に書いてある。

In the rare case that you intentionally want to change the state of another component as a result of rendering, you can wrap the setState call into useEffect.

レンダリングの結果として意図的に別のコンポーネントの状態を変更したいというまれなケースでは、setState呼び出しをuseEffectにラップできます。

要するに、setStateをuseEffectでラップすれば良いらしい。
これだと、Warningは出ない。

App.tsx
import React, { useState, useEffect } from "react";
interface Props {
  setCount: Function;
}
const Fuge: React.FC<Props> = ({ setCount }) => {
  useEffect(() => {
    setCount(1);
  }, [setCount]);
  return <p>test</p>;
};
const Hoge: React.FC = () => {
  const [count, setCount] = useState(0);
  return <Fuge setCount={setCount} />;
};
export default Hoge;

無事解決!
ふぅ、焦ったー。。。

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