React Learning
以下の 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!'
}
};
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 の視点からは等価
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')
);
- <Welcome name="Sara" /> という要素を引数として ReactDOM.render() を呼び出します。
- React は Welcome コンポーネントを呼び出し、そのときに props として {name: 'Sara'} を渡します。
- Welcome コンポーネントは出力として <h1>Hello, Sara</h1> 要素を返します。
- React DOM は <h1>Hello, Sara</h1> に一致するよう、DOM を効率的に更新します。
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')
);
<Welcome name="Sara" />
でComponentが呼び出されるときにprops
に属性name
が付与(props.name = "Sara"
)されて渡される。
progate
use higher letter case in component like App.js or Language.js
関数としてコンポーネントを定義する場合は引数にprops
をつけて、クラスとして定義する場合は不要でthis.props
で参照できるのか?
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Language extends React.Component {
render() {
return (
<div className='language-item'>
<div className='language-name'>{this.props.name}</div>
</div>
);
}
}
export default Language;
exportはファイルをまたぐ場合はどちらもいるようだ。
<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"
のようにダブルクォーテーションが必要)
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のこと
);
}
}
モーダル表示のTrue/FalseなどでReactのStateを使うケースが出てくるが、クラスで定義するコンポーネントにおいて、props
の初期化(construct)は暗黙的に行われているっぽい。stateを使うときだけその省略が解かれて書かなければならなくなる。
class Lesson extends React.Component {
constructor(props) {
super(props);
this.state = {name: "HTML Lesson"}
}
render() {
return(
<p>{this.state.name}</p>
)
}
}
stateを使わないケース
class Lesson extends React.Component {
// constructor(props) { // 暗黙的に省略されているっぽい
// super(props);
// }
render() {
return(
<p>CSSだよー!</p>
)
}
}
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;
なおプログラミング言語における関数と、オブジェクト指向におけるメソッドは別物であり、「オブジェクトのプロパティとして持たせた関数がメソッド」である。
JavaScriptにおいてClass内のメソッド定義にはfunctionキーワードは不要。だから上記でhandleClick() { }
といきなり素で書けるのだ。
公式の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')
);
親コンポーネントであれ子コンポーネントであれ、特定の他のコンポーネントがステートフルかステートレスかを知ることはできませんし、特定のコンポーネントの定義が関数型かクラス型かを気にするべきではありません。
これが、state はローカルのものである、ないしはカプセル化されている、と言われる理由です。state を所有してセットするコンポーネント自身以外からはその state にアクセスすることができません。
ただしpropsであればrender時に渡せるので
コンポーネントはその子コンポーネントに props として自身の state を渡してもかまいません。
コンポーネントツリーとは props が流れ落ちる滝なのだと想像すると、各コンポーネントの state とは任意の場所で合流してくる追加の水源であり、それらもまた下に流れ落ちていくものなのです。
preventDefault
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked, but prevented.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
it means that stopping event obiously, but i dont know why i did write this memo.
本来はクラス内のメソッドで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>
);
}
}
// 省略
この構文について
this.handleClick = this.handleClick.bind(this);
The bind() method creates a new function that, when called, has its
this
keyword set to the provided value,
上記で過去のステートを使えているのはsetState()
の2番目の形式を使っているから。
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
上記では第二引数のprops
がnullになっている。
Reactの流儀(チュートリアル)
検索可能な商品データ表
- データは上流から下流へpropsとして流れる(単方向データフロー)
- 既存のstateを使って計算できるものはstateにしない
1. UI をコンポーネントの階層構造に落とし込む
2. Reactで静的なバージョンを作成する
props は親から子へとデータを渡すための手段です。もし、あなたが state に慣れ親しんでいる場合でも、今回の静的なバージョンを作る上では一切 state を使わないでください。state はユーザ操作や時間経過などで動的に変化するデータを扱うために確保されている機能です。
3. UI 状態を表現する必要かつ十分な state を決定する
- 元となる商品のリスト
- ユーザが入力した検索文字列(state)
- チェックボックスの値(state)
- フィルタ済みの商品のリスト
4. state をどこに配置するべきなのかを明確にする
5. 逆方向のデータフローを追加する
TypeScript
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>
);
}
ステートフック
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 はオブジェクトである必要はないことに注意
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 にアクセスすることが可能です。デフォルトでは初回のレンダーも含む毎回のレンダー時にこの副作用関数が呼び出されます
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);
Q. 副作用内からなぜ関数を返したのか?
A. useEffect()がそういう風に設計されているから
これこそが副作用のクリーンアップのためのオプションの仕組みです。すべての副作用は、それをクリーンアップするための関数を返すことができます。これにより購読を開始するためのロジックと解除するためのロジックを並べて書くことができます。両方とも同じ副作用の一部なのです!
https://ja.reactjs.org/docs/hooks-effect.html#example-using-hooks-1
DOMのマウントと更新(≒render)に直接関係しない処理のことを総じて副作用と呼んでいるっぽい
データの取得、購読 (subscription) の設定、あるいは React コンポーネント内の DOM の手動での変更、といったものはすべて副作用の例
https://ja.reactjs.org/docs/hooks-effect.html
useEffect は毎回のレンダー後に呼ばれる
importの中括弧
- export defaultで公開した値は
-- 中括弧なしでimportできる
-- どんな名前でimportしてもよい- ただのexportで公開した値は
-- 中括弧をつけてimportする
-- export時の変数名でimportする
-- { xxx as yyy }とすることで任意の変数名に変えることができる
https://qiita.com/ozaki25/items/9723cb3c1c72845157d5