# React reconciliation

در پروژه‌های React، نقطه‌ی آغازین فرآیند rendering، قطعه کد زیر میباشد که درون فایل index.js قرار دارد: ReactDOM.render(<App />, document.getElementById('root')); توسط متد ReactDOM.render یک وهله از ک

- Published: 2019-09-18
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-3104

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/3104) منتشر شده است.

<div class="postBody"><div>در پروژه‌های React، نقطه‌ی آغازین فرآیند rendering، قطعه کد زیر میباشد که درون فایل index.js قرار دارد:</div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">ReactDOM.render(&lt;App /&gt;, document.getElementById('root'));</pre>
 </div>
  <br/> <div>توسط متد ReactDOM.render یک وهله از کامپوننت App ایجاد شده و منجر به فراخوانی متد render کامپوننت مربوطه خواهد شد. درون متد ذکر شده ممکن است چندین کامپوننت تعریف شده باشند. در نتیجه به ازای هر کامپوننت، متد render متناظر با آن فراخوانی خواهد شد. در نهایت یک ساختار سلسله مراتبی از عناصر HTML تشکیل شده و توسط آرگومان دوم متد فوق، درون عنصری با آی‌دی root درج خواهند شد. <br/> </div> <div>برای مثال کامپوننت‌های زیر را در نظر بگیرید که هر کدام درون فایل مربوط به خودشان هستند:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">// List.js&#10;import React, { Component } from 'react';&#10;import { ActionButton } from './ActionButton';&#10;export class List extends Component {&#10;    constructor(props) {&#10;        super(props);&#10;        this.state = {&#10;            items: [&#10;                { id: 1, title: "Item 1" },&#10;                { id: 2, title: "Item 2" },&#10;                { id: 3, title: "Item 3" },&#10;                { id: 4, title: "Item 4" },&#10;                { id: 5, title: "Item 5" },&#10;            ]&#10;        };&#10;    }&#10;&#10;    reverse = () =&gt; {&#10;        this.setState({ items: this.state.items.reverse() });&#10;    }&#10;&#10;    render() {&#10;        console.log("Render List Component");&#10;        return (&#10;            &lt;div className="list-container"&gt;&#10;                &lt;ActionButton callback={this.reverse} /&gt;&#10;                &lt;ul&gt;&#10;                    {this.state.items.map(item =&gt; {&#10;                        return &lt;li key={item.id}&gt;&#10;                            {item.title}&#10;                        &lt;/li&gt;&#10;                    })}&#10;                &lt;/ul&gt;&#10;            &lt;/div&gt;&#10;        );&#10;    }&#10;}&#10;&#10;// ActionButton.js&#10;import React, { Component } from 'react';&#10;export class ActionButton extends Component {&#10;    render() {&#10;        console.log("Render ActionButton Component");&#10;        return (&#10;            &lt;button onClick={this.props.callback}&gt;Click me&lt;/button&gt;&#10;        );&#10;    }&#10;}</pre>
 </div> </div> <div>در نهایت درون کامپوننت App.js نیز همچین ساختاری خواهیم داشت:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">import React from 'react';&#10;import './App.css';&#10;import { List } from './List';&#10;&#10;function App() {&#10;  console.log("Render App Component");&#10;  return (&#10;    &lt;div className="App"&gt;&#10;      &lt;h1&gt;Reconciliation Process&lt;/h1&gt;&#10;      &lt;List /&gt;&#10;    &lt;/div&gt;&#10;  );&#10;}&#10;&#10;export default App;</pre>
 </div> <br/> </div> <div> <img src="/img/dntips/1ff13de23c4622847493.png" style="display: block; margin-left: auto; margin-right: auto; cursor: nw-resize; height: 331px;"/>   </div> <div>درون متد render هر کدام از کامپوننت‌های فوق یک console.log نوشته شده است. با اجرای برنامه خروجی زیر در کنسول مرورگر قابل مشاهده است:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Render App Component&#10;Render List Component&#10;Render ActionButton Component</pre>
 </div> <br/> </div> <div>دلیل آن نیز این است که با اجرای اپلیکیشن، React از تمامی کامپوننت‌ها درخواست فراخوانی متد renderشان را خواهد کرد. به محض اینکه محتوای HTML درون صفحه نمایش داده شد، اپلیکیشن در وضعیتی تحت عنوان reconciled قرار خواهد گرفت؛ در این مرحله، خروجی نمایش داده شده با state کامپوننت‌ها سازگار است. React منتظر خواهد بود تا تغییری رخ دهد که در بیشتر اپلیکیشن‌ها این تغییر توسط کاربر انجام خواهد گرفت که در نهایت منجر به فراخوانی متد setState میشود. متد setState در واقع state dataی یک کامپوننت را بروزرسانی میکند؛ اما اینکار کامپوننت را stale یا کهنه میکند. یعنی محتوای HTMLی نمایش داده شده به کاربر قدیمی خواهد شد و با تنها یک رخداد ممکن است چندین state data تغییر کنند. این کار باعث خواهد شد که متد render برای تمامی کامپوننت‌های تغییر کرده فراخوانی شود. اما از آنجائیکه بروزرسانی DOM عملی هزینه‌بر است، در نتیجه React محتوای قبلی (کش شده با عنوان Virtual DOM) را با محتوای جدید مقایسه میکند تا کمترین میزان بروزرسانی DOM را داشته باشد. به این فرآیند <b>Reconciliation</b> گفته میشود.</div> <div>برای درک بهتر این فرآیند، درون کامپوننت List یک آی‌دی به عنصر ul اضافه خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">&lt;ul id="list"&gt;&#10;    {this.state.items.map(item =&gt; {&#10;        return &lt;li key={item.id}&gt;&#10;            {item.title}&#10;        &lt;/li&gt;&#10;    })}&#10;&lt;/ul&gt;</pre>
 </div> <br/> </div> <div>اکنون در حالیکه پروژه در حال اجرا است، کد زیر را درون کنسول مرورگر اجرا کنید: <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">document.getElementById("list").classList.add("message")</pre>
 </div> <br/> <p style="
direction:rtl;unicode-bidi:embed">همانطور که مشخص است کد فوق کلاسی با نام message را به عنصر ul اضافه کرده است:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSS" name="code">.message {&#10;  border: 1px solid green;&#10;  padding: 2rem;&#10;}</pre>
 </div> <p style="
direction:rtl;unicode-bidi:embed"> <br/> </p> <p style="
direction:rtl;unicode-bidi:embed">اکنون وقتی بر روی دکمه Click me کلیک کنیم، محتوای درون کامپوننت فوق تغییر پیدا میکند، اما عنصر ul همچنان دارای کلاس message است؛ دلیل آن نیز همانطور که عنوان شد این است که React محتوای تولید شده توسط کامپوننت List را با Virtual DOM خودش مقایسه میکند و چون از لحاظ ساختار DOM با هم برابر هستند تغییری در ساختار خروجی کامپوننت ایجاد نمیکند و فقط قسمت‌هایی را که تغییر کرده‌اند، بروزرسانی خواهد کرد.<br/> </p> <p style="
direction:rtl;unicode-bidi:embed"> <br/> </p> <p style="
direction:rtl;unicode-bidi:embed"> </p> <p style="
direction:rtl;unicode-bidi:embed">اکنون کامپوننت فوق را اینگونه تغییر خواهیم داد:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">import React, { Component } from 'react';&#10;import { ActionButton } from './ActionButton';&#10;export class List extends Component {&#10;    constructor(props) {&#10;        // as before&#10;    }&#10;&#10;    reverse = () =&gt; {&#10;        this.setState({ items: this.state.items.reverse(), wrapInDiv: true });&#10;    }&#10;&#10;    generateElement = () =&gt; {&#10;        const list = &lt;ul id="list"&gt;&#10;            {this.state.items.map(item =&gt; {&#10;                return &lt;li key={item.id}&gt;&#10;                    {item.title}&#10;                &lt;/li&gt;&#10;            })}&#10;        &lt;/ul&gt;;&#10;&#10;        return this.state.wrapInDiv ? &lt;div&gt;{list}&lt;/div&gt; : list;&#10;    }&#10;&#10;    render() {&#10;        console.log("Render List Component");&#10;        return (&#10;            &lt;div className="list-container"&gt;&#10;                &lt;ActionButton callback={this.reverse} /&gt;&#10;                {this.generateElement()}&#10;            &lt;/div&gt;&#10;        );&#10;    }&#10;}</pre>
 </div> <br/> <div>   در اینجا اگر مجدداً کلاس ذکر شده را به عنصر ul اضافه کنیم و سپس بر روی دکمه کلیک کنیم، خواهیم دید که کلاس message از عنصر ul حذف خواهد شد. دلیل آن نیز این است که ساختار HTML با Virtual DOM یکی نیست و React کامپوننت موردنظر را با تغییرات جدید مجدداً رندر خواهد کرد. <br/> </div> <div> <br/> </div> <div> <b>نکته: </b>برای مشاهده تغییرات DOM می‌توانید قابلیت Paint flashing را در مرورگر Chrome از قسمت Developer Tool &gt; More tools &gt; Rendering فعال کنید و تغییرات را به صورت visual ببینید.</div> </div></div>
