一、原理#
まず、render
関数はreact
内で 2 つの形式があります:
クラスコンポーネントでは、render
メソッドを指します:
class Foo extends React.Component {
render() {
return <h1> Foo </h1>;
}
}
関数コンポーネントでは、関数コンポーネント自体を指します:
function Foo() {
return <h1> Foo </h1>;
}
render
内では、jsx
を記述します。jsx
はbabel
によってコンパイルされ、私たちが知っているjs
形式に変換されます。以下のようになります:
return (
<div className='cn'>
<Header> hello </Header>
<div> start </div>
Right Reserve
</div>
)
babel
によるコンパイル後:
return (
React.createElement(
'div',
{
className : 'cn'
},
React.createElement(
Header,
null,
'hello'
),
React.createElement(
'div',
null,
'start'
),
'Right Reserve'
)
)
名前からわかるように、createElement
メソッドは要素を作成するために使用されます。
react
では、この要素は仮想 DOM ツリーのノードであり、3 つのパラメータを受け取ります:
-
type:タグ
-
attributes:タグの属性、存在しない場合は null
-
children:タグの子ノード
これらの仮想 DOM ツリーは最終的に実際の DOM にレンダリングされます。
render
プロセスでは、React
は新しく呼び出されたrender
関数から返されたツリーと古いバージョンのツリーを比較し、これはDOM
をどのように更新するかを決定するための必要なステップです。その後、diff
比較が行われ、DOM
ツリーが更新されます。
二、トリガータイミング#
render
の実行タイミングは主に 2 つに分けられます:
- クラスコンポーネントでの setState による状態の変更
class Foo extends React.Component {
state = { count: 0 };
increment = () => {
const { count } = this.state;
const newCount = count < 10 ? count + 1 : count;
this.setState({ count: newCount });
};
render() {
const { count } = this.state;
console.log("Foo render");
return (
<div>
<h1> {count} </h1>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
ボタンをクリックすると、setState
メソッドが呼び出され、count
が変化していない場合でもコンソールにFoo render
が出力され、render
が実行されることが証明されます。
- useState フックを使用した関数コンポーネントでの状態の変更
function Foo() {
const [count, setCount] = useState(0);
function increment() {
const newCount = count < 10 ? count + 1 : count;
setCount(newCount);
}
console.log("Foo render");
return (
<div>
<h1> {count} </h1>
<button onClick={increment}>Increment</button>
</div>
);
}
関数コンポーネントでは、useState
を使用してデータを更新する形式で、配列の値が変更されない限り、render
はトリガされません。
- クラスコンポーネントの再レンダリング
class App extends React.Component {
state = { name: "App" };
render() {
return (
<div className="App">
<Foo />
<button onClick={() => this.setState({ name: "App" })}>
Change name
</button>
</div>
);
}
}
function Foo() {
console.log("Foo render");
return (
<div>
<h1> Foo </h1>
</div>
);
}
App
コンポーネント内のChange name
ボタンをクリックすると、Foo
の具体的な実装に関係なく、render
が再度実行されます。
- 関数コンポーネントの再レンダリング
function App(){
const [name,setName] = useState('App')
return (
<div className="App">
<Foo />
<button onClick={() => setName("aaa")}>
{ name }
</button>
</div>
)
}
function Foo() {
console.log("Foo render");
return (
<div>
<h1> Foo </h1>
</div>
);
}
useState
を使用して状態を更新する場合、最初のレンダリング時にのみFoo render
がトリガされ、後続の更新ではFoo render
が発生しないことがわかります。
三、まとめ#
render
関数ではJSX
を記述し、createElement
のような形式に変換して仮想 DOM を生成し、最終的に実際の DOM に変換します。
React
では、クラスコンポーネントでsetState
メソッドが実行されると必ずrender
関数が実行されますが、関数コンポーネントではuseState
を使用して状態を変更しても必ずしもrender
がトリガされるわけではありません。
コンポーネントのprops
が変更された場合、必ずしもrender
関数が実行されるわけではありませんが、props
の値が親コンポーネントまたは祖先コンポーネントのstate
から取得される場合、
この場合、親コンポーネントまたは祖先コンポーネントのstate
が変更されると、子コンポーネントが再レンダリングされます。
したがって、setState
が実行されるとrender
メソッドが実行され、useState
は現在の値が変更されたかどうかを判断してrender
メソッドを実行します。親コンポーネントがレンダリングされると、子コンポーネントもレンダリングされます。