React coreの実装を読みたい
良さそうな記事達
Prerequisites
I strongly suggest that you are familiar with the following resources before continuing:
React Components, Elements, and Instances - "Component" is often an overloaded term. A firm grasp of these terms is crucial.
Reconciliation - A high-level description of React's reconciliation algorithm.
React Basic Theoretical Concepts - A description of the conceptual model of React without implementation burden. Some of this may not make sense on first reading. That's okay, it will make more sense with time.
React Design Principles - Pay special attention to the section on scheduling. It does a great job of explaining the why of React Fiber.
らしいので、読む
→ スクラップにまとめた → スクラップにまとめた
Pete Huntさんのこれも
これもやな
React Components, Elements, and Instances
Managing the Instances
traditionl UI model
- 各コンポーネントのインスタンスはDOMノードと子コンポーネントのインスタンスへの参照を保持し、適切なタイミングで作成・更新・削除が必要
- 親から子へインスタンスへの直接アクセスをするため切り離しが困難
Elements Describe the Tree
ElementはコンポーネントのインスタンスまたはDOMノード(buttonとか)とそのプロパティ(色とか)を記述したObject
→ 画面表示したいものをReactに伝える
DOM Elements
例
HTML
<button class='button button-blue'>
<b>
OK!
</b>
</button>
↓
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
type:string、タグとしてのDOMのノード
props:属性
Reactの要素はtraverseがかんたんで、実際のDOM要素より軽い → 単なるオブジェクトなので
note
Traverse: DOMツリーを上・下で様々な処理をすること
Component Elements
typeはfunctionやclassであってもよい
{
type: Button,
props: {
color: 'blue',
children: 'OK!'
}
}
これが Reactのcore の考え
componentもelementも混在させることができる
これにより、componentは互いに切り離され、is-aとhas-a関係を表現できる
Components Encapsulate Element Trees
Reactはcompoenntになんのelementをrenderすればよいか尋ねて、それを底のDOMタグまで繰り返す。
React componentはpropsが入力でElementツリーが出力なだけ。
const Form = ({ isSubmitted, buttonText }) => {
if (isSubmitted) {
// Form submitted! Return a message element.
return {
type: Message,
props: {
text: 'Success!'
}
};
}
// Form is still visible! Return a button element.
return {
type: Button,
props: {
children: buttonText,
color: 'blue'
}
};
};
Reactがインスタンスの生成・更新・破棄等のインスタンス管理をする。
Components Can Be Classes or Functions
以下の3つは全部ほぼ同じ。
// 1) As a function of props
const Button = ({ children, color }) => ({
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
});
// 2) Using the React.createClass() factory
const Button = React.createClass({
render() {
const { children, color } = this.props;
return {
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
};
}
});
// 3) As an ES6 class descending from React.Component
class Button extends React.Component {
render() {
const { children, color } = this.props;
return {
type: 'button',
props: {
className: 'button button-' + color,
children: {
type: 'b',
props: {
children: children
}
}
}
};
}
}
気になる記述
When a component is defined as a class, it is a little bit more powerful than a function component. It can store some local state and perform custom logic when the corresponding DOM node is created or destroyed.
functionがシンプルなので推奨
Top-Down Reconciliation
Form componentが以下で定義されるとして
const Form = ({ isSubmitted, buttonText }) => {
if (isSubmitted) {
// Form submitted! Return a message element.
return {
type: Message,
props: {
text: 'Success!'
}
};
}
// Form is still visible! Return a button element.
return {
type: Button,
props: {
children: buttonText,
color: 'blue'
}
};
};
FormのReactDOM.render
が呼ばれると
ReactDOM.render({
type: Form,
props: {
isSubmitted: false,
buttonText: 'OK!'
}
}, document.getElementById('root'));
propsのisSubmittedがfalseなので、
→ type: Button が呼ばれる → Buttonはtype: 'button'のDOMタグObjectを返却する。
これが ReactDOM.render
や setState()
が呼ばれた時に 実行される Reconciliation のプロセス
Reconciliationが終わると ReactはDOMツリーの結果を知り、react-domがDOMノードのアップデートを行う。
これがReactアプリが最適化(optimize)しやすい理由の一部
コンポーネントツリーが大きくなっても、関連するpropが変更されてなければ差分をスキップする。
→ Reactとimmutabilityの相性はバツグン
インスタンスの説明が少ないが、実はReactはインスタンスの重要性は他のオブジェクト指向UIフレームワークよりはるかに低い
親コンポーネントのインスタンスが子コンポーネントのインスタンスにアクセスするメカニズムはあるが、一般的には避けるべき
Reconciliation
React’s “diffing” algorithm
Motivation
render() functionがReact elementsのツリーを作成する。
stateやpropsの更新時はrender()が異なるツリーを返す。
その時にUIを効率的に更新する方法が必要。
state of the art algorithms
order: O(n3)
→ これをReactで使うと 1000個の要素に対して、10億回単位の比較が必要
Reactは2つの仮定に基づくヒューリスティックなO(n) algorithmを実装している
2つの仮定
Two elements of different types will produce different trees.
The developer can hint at which child elements may be stable across different renders with a key prop.
The Diffing Algorithm
Elements Of Different Types
例:divとspanでルートDOMが違う時
<div>
<Counter />
</div>
<span>
<Counter />
</span>
古いツリーを解体し、新しいツリーを1から構築する
古いCounterは消されて、新しいCounterで再マウントされる
DOM Elements Of The Same Type
例:divとdivで同じElementの時
<div className="before" title="stuff" />
<div className="after" title="stuff" />
attributesだけ更新する(例ではclassNameのみ更新する)
Component Elements Of The Same Type
コンポーネントが更新されると、インスタンスは同じままなのでレンダリング間で状態が維持される。
Reactは新しい要素に一致するように基礎となるコンポーネントインスタンスのpropsを更新し、基礎となるインスタンスでUNSAFE_componentWillReceiveProps()、UNSAFE_componentWillUpdate()、および componentDidUpdate() を呼び出す。
次にrender()が呼び出され、diffアルゴリズムが再帰的に処理される。
Recursing On Children
デフォルトでは、DOMノードの子を再帰的に処理する場合、Reactは両方の子リストを同時に反復処理し、違いがあるたびに変異を発生させます。
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
この例だとthirdを入れるだけ
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
でも、この例だと全部置き換えないといけない...
Keys
上記の問題を解決するためにKey属性がある
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
keyがあれば、何が新しいかわかるので最初にinsertされてもReactは検知できる
keyにはuniqueIDが好ましい
最終手段として配列のインデックスを使うことが出来るが、項目の順番を変えると非効率になる
Tradeoffs
- The algorithm will not try to match subtrees of different component types. If you see yourself alternating between two component types with very similar output, you may want to make it the same type. In practice, we haven’t found this to be an issue.
- Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
React - Basic Theoretical Concepts
Transformation
Reactの大前提は、UIはデータを別の形に投影したものに過ぎない
同じ入力は同じ出力をする。
Abstraction
再利用可能なピースに抽象化することが重要
ある関数から別の関数を呼ぶような場合
Composition
他の抽象化を組み合わせて新しいものを作る
State
いいね
なんだか良さそう
これは一番最初に読むと良いかも