Closed29

React Learning

まった | maztak.ethまった | maztak.eth

以下の 2 つの例は等価

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

本質的には以下のようなオブジェクトを生成してる

// Note: this structure is simplified
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};
まった | maztak.ethまった | maztak.eth
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// ES6 クラスも使用できます:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

上記 2 つのコンポーネントは React の視点からは等価

まった | maztak.ethまった | maztak.eth

JSX Basic Usage?

most simple(maybe no meanings)

const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

use element variable

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

ReactDOM.render(
  element,
  document.getElementById('root')
);

call function

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}


const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

use component that difinited as functions

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);
  1. <Welcome name="Sara" /> という要素を引数として ReactDOM.render() を呼び出します。
  2. React は Welcome コンポーネントを呼び出し、そのときに props として {name: 'Sara'} を渡します。
  3. Welcome コンポーネントは出力として <h1>Hello, Sara</h1> 要素を返します。
  4. React DOM は <h1>Hello, Sara</h1> に一致するよう、DOM を効率的に更新します。
まった | maztak.ethまった | maztak.eth

use only component (not variable) and that's children component

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
まった | maztak.ethまった | maztak.eth

<Welcome name="Sara" />でComponentが呼び出されるときにpropsに属性nameが付与(props.name = "Sara")されて渡される。

まった | maztak.ethまった | maztak.eth

関数としてコンポーネントを定義する場合は引数にpropsをつけて、クラスとして定義する場合は不要でthis.propsで参照できるのか?

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
src/component/Language.js
class Language extends React.Component {
  render() {
    return (
      <div className='language-item'>
        <div className='language-name'>{this.props.name}</div>        
      </div>
    );
  }
}

export default Language;

https://prog-8.com

exportはファイルをまたぐ場合はどちらもいるようだ。

まった | maztak.ethまった | maztak.eth
      <div className='language'>
          {languageList.map((languageItem) => {
              return (<Language name={languageItem.name} image={languageItem.image} />)
          })}
        </div>
      </div>
  • mapの外側はJavaScript(ES6, JSXではない?)なので中括弧で囲むこと
  • 返り値がcomponent(JSX)のため、1行でもreturn()が必要
  • componentの属性には中括弧が必要(文字列で渡すときはname="HTML"のようにダブルクォーテーションが必要)
まった | maztak.ethまった | maztak.eth

thisって正確に言うと何?

thisには4種類あって、関数の呼び出し方などによって変わるが、Reactにおいては「メソッド呼び出し元であるlessonのこと」であってると思う。

<Lesson
    name="HTML"
    image="https://html.jp"
 />

// 内部的に起こってそうなことを無理やり書くと
// インスタンスlessonがrender()を読んでる感じで、Reactの仕組みでlessonはpropsオブジェクトを持ってる
console.log(lesson.props) // -> {name: "HTML", image: "https://html.jp"}
lesson.render()
class Lesson extends React.Component {
  render() {
    return (
          <p>{this.props.name}</p> // thisとはメソッド呼び出し元であるlessonのこと
    );
  }
}

https://qiita.com/takkyun/items/c6e2f2cf25327299cf03

まった | maztak.ethまった | maztak.eth

モーダル表示のTrue/FalseなどでReactのStateを使うケースが出てくるが、クラスで定義するコンポーネントにおいて、propsの初期化(construct)は暗黙的に行われているっぽい。stateを使うときだけその省略が解かれて書かなければならなくなる。

src/component/Lesson.js
class Lesson extends React.Component {

  constructor(props) {
    super(props);
    this.state = {name: "HTML Lesson"}
  }

  render() {
    return(
      <p>{this.state.name}</p>
    )
  }
}

stateを使わないケース

src/component/Lesson.js
class Lesson extends React.Component {

  // constructor(props) {  // 暗黙的に省略されているっぽい
  //   super(props);
  // }

  render() {
    return(
      <p>CSSだよー!</p>
    )
  }
}
まった | maztak.ethまった | maztak.eth

thisはつまりswiftでいうself.
classが持つプロパティ(変数や定数)やメソッドにアクセスするために使う。

下記の例においてthisが省略できないのはrender() { return () }の中でメソッドを呼んでいるから。

import React from 'react';

class Lesson extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isModalOpen: false};
  }
  
  handleClickLesson() {
    this.setState({isModalOpen: true})
  }
  
  render() {
    let modal;
    if (this.state.isModalOpen) {
      modal = (
              <p>{this.props.introduction}</p>       
      );
    }

    return (
      <div className='lesson-card'>
        <div
          className='lesson-item'
          onClick={ () => {this.handleClickLesson()} }
        >
          <p>{this.props.name}</p>
        </div>
        {modal}
      </div>
    );
  }
}

export default Lesson;

https://prog-8.com/react/study/3/8#/27

まった | maztak.ethまった | maztak.eth

公式のState例

ただしこれではリロードしたタイミングでしか日付時刻を取得・更新できない。

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

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

タイマーを設定したいのは、最初に Clock が DOM として描画されるときです。このことを React では “マウント (mounting)” と呼びます。

またタイマーをクリアしたいのは、Clock が生成した DOM が削除されるときです。このことを React では “アンマウント (unmounting)” と呼びます。

ライフサイクルメソッド (lifecycle method)”

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval( // 戻り値はclearInterval()メソッドをもつintervalID
      () => this.tick(),
      1000 // これはインターバル間隔。ミリ秒単位なので1秒のインターバル=秒針ということ
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
まった | maztak.ethまった | maztak.eth

親コンポーネントであれ子コンポーネントであれ、特定の他のコンポーネントがステートフルかステートレスかを知ることはできませんし、特定のコンポーネントの定義が関数型かクラス型かを気にするべきではありません。

これが、state はローカルのものである、ないしはカプセル化されている、と言われる理由です。state を所有してセットするコンポーネント自身以外からはその state にアクセスすることができません。

ただしpropsであればrender時に渡せるので

コンポーネントはその子コンポーネントに props として自身の state を渡してもかまいません。

まった | maztak.ethまった | maztak.eth

コンポーネントツリーとは props が流れ落ちる滝なのだと想像すると、各コンポーネントの state とは任意の場所で合流してくる追加の水源であり、それらもまた下に流れ落ちていくものなのです。

まった | maztak.ethまった | maztak.eth

preventDefault

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked, but prevented.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
まった | maztak.ethまった | maztak.eth

本来はクラス内のメソッドでthisを使いたい場合はconstructor内でbind()が必要(こちらが推奨される)

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

アロー関数を使うとbindしなくてもよい(Progateではこっちを使用していた)

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
  }

  handleClick() {
    // 省略
  }

  render() {
    return (
      <button onClick={() => {this.handleClick()}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

// 省略

https://ja.reactjs.org/docs/handling-events.html

まった | maztak.ethまった | maztak.eth

上記で過去のステートを使えているのはsetState()の2番目の形式を使っているから。

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

上記では第二引数のpropsがnullになっている。

まった | maztak.ethまった | maztak.eth

Reactの流儀(チュートリアル)

検索可能な商品データ表
FilterableProductTable
https://ja.reactjs.org/docs/thinking-in-react.html

  • データは上流から下流へpropsとして流れる(単方向データフロー)
  • 既存のstateを使って計算できるものはstateにしない

1. UI をコンポーネントの階層構造に落とし込む

2. Reactで静的なバージョンを作成する

props は親から子へとデータを渡すための手段です。もし、あなたが state に慣れ親しんでいる場合でも、今回の静的なバージョンを作る上では一切 state を使わないでください。state はユーザ操作や時間経過などで動的に変化するデータを扱うために確保されている機能です。

3. UI 状態を表現する必要かつ十分な state を決定する

  • 元となる商品のリスト
  • ユーザが入力した検索文字列(state)
  • チェックボックスの値(state)
  • フィルタ済みの商品のリスト

4. state をどこに配置するべきなのかを明確にする

5. 逆方向のデータフローを追加する

まった | maztak.ethまった | maztak.eth

hook

フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります

フックとは、関数コンポーネントに state やライフサイクルといった React の機能を “接続する (hook into)” ための関数です。フックは React をクラスなしに使うための機能ですので、クラス内では機能しません。

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0); // このuseStateという関数がフック

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

https://ja.reactjs.org/docs/hooks-intro.html

まった | maztak.ethまった | maztak.eth

ステートフック

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

この例の useState が フック(この単語の意味するところはすぐ後で説明します)です。関数コンポーネントの中でローカルな state を使うために呼び出しています。この state は以降の再レンダーの間も React によって保持されます。useState は現在の state の値と、それを更新するための関数とをペアにして返します。

クラスコンポーネントにおける this.setState と似ていますが、新しい state が古いものとマージされないという違いがあります

  • useState 引数は state の初期値
  • this.state と違い、state はオブジェクトである必要はないことに注意
まった | maztak.ethまった | maztak.eth

useEffect

これまでに React コンポーネントの内部から、外部データの取得や購読 (subscription)、あるいは手動での DOM 更新を行ったことがおありでしょう。これらの操作は他のコンポーネントに影響することがあり、またレンダーの最中に実行することができないので、われわれはこのような操作を “副作用 (side-effects)“、あるいは省略して “作用 (effects)” と呼んでいます。

useEffect は副作用のためのフックであり、関数コンポーネント内で副作用を実行することを可能にします。クラスコンポーネントにおける componentDidMount, componentDidUpdate および componentWillUnmount と同様の目的で使うものですが、1 つの API に統合されています

例えば、このコンポーネントは React が DOM を更新した後で、HTML ドキュメントのタイトルを設定します。

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect を呼ぶことで、DOM への更新を反映した後にあなたが定義する「副作用関数」を実行するように React に指示します。副作用はコンポーネント内で宣言されるので、props や state にアクセスすることが可能です。デフォルトでは初回のレンダーも含む毎回のレンダー時にこの副作用関数が呼び出されます

https://ja.reactjs.org/docs/hooks-overview.html#effect-hook

まった | maztak.ethまった | maztak.eth

useEffectは自分をクリーンアップする関数をオプションとして渡すことができる

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

useEffect()の定義は

(alias) useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void

// ちなみに (parameter) deps: DependencyList
// If present, effect will only activate if the values in the list change.

だから、分かりやすく書き直すとこう?

const subscribe = () => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return cleanup;
}

const cleanup = () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};

useEffect(effect: subscribe);
まった | maztak.ethまった | maztak.eth

Q. 副作用内からなぜ関数を返したのか?
A. useEffect()がそういう風に設計されているから

これこそが副作用のクリーンアップのためのオプションの仕組みです。すべての副作用は、それをクリーンアップするための関数を返すことができます。これにより購読を開始するためのロジックと解除するためのロジックを並べて書くことができます。両方とも同じ副作用の一部なのです!
https://ja.reactjs.org/docs/hooks-effect.html#example-using-hooks-1

まった | maztak.ethまった | maztak.eth

DOMのマウントと更新(≒render)に直接関係しない処理のことを総じて副作用と呼んでいるっぽい

データの取得、購読 (subscription) の設定、あるいは React コンポーネント内の DOM の手動での変更、といったものはすべて副作用の例
https://ja.reactjs.org/docs/hooks-effect.html

useEffect は毎回のレンダー後に呼ばれる

このスクラップは2021/12/29にクローズされました