بررسی عملی چرخه‌ی زندگی کامپوننت ها: ایجاد کامپوننت (Creation)

A Practical Study of the Life Cycle of Components: Creation

01 آبان 1398
بررسی عملی چرخه ی زندگی کامپوننت ها: ایجاد کامپوننت (creation)

در جلسات قبلی به شما گفتیم که چرخه زندگی یا lifecycle کامپوننت ها چندین قسمت دارد و برای راحت تر شدن بحث آن را به چند قسمت تقسیم می کنیم. قسمت اول، creation یا ایجاد کامپوننت در ری اکت و متدهایشان است. سپس در مورد انواع متدهای اجرایی برای ساخت کامپوننت ها صحبت کردیم بنابراین حالا زمان بررسی عملی آن ها در پروژه خودمان است.

همانطور که می دانید تمام این فرآیندها به صورت خودکار و پشت پرده اتفاق می افتد بنابراین نیازی به دخالت ما نیست (مگر اینکه بخواهیم کار خاصی انجام دهیم). دلیل دخالت ما در این جلسه این است که روند اجرای برنامه را ببینیم و بیشتر با آن آشنا شویم. اجرای عملی این متدها باعث می شود درک شما از جزئیات و فرآیندهای react بسیار بیشتر شود و توانایی حل مشکل شما نیز ارتقاء پیدا کند.

فایل App.js را باز کنید. این کامپوننت یک کامپوننت کلاس-محور است بنابراین در آن به lifecycle hooks دسترسی داریم. اگر از جلسه قبل یادتان باشد اولین چیزی که اجرا می شود constructor است، بنابراین:

class App extends Component {
  constructor (props) {
    super(props);
  }

  state = {...

گفته بودیم که برای استفاده از props درون constructor باید از (super(props استفاده کنیم. وظیفه Super اجرا کردن constructor شیء Component از کتابخانه React است (همان شیء ای که extend شده است). برای تست کردن این constructor می توانیم یک پیام ساده را در console نمایش دهیم:

class App extends Component {
  constructor (props) {
    super(props);
    console.log('[App.js] constructor');
  }

اگر به مرورگر بروید می توانید این پیام را مشاهده کنید. یادتان باشد که constructor به محض اجرای این کلاس اجرا خواهد شد (اولین چیزی است که اجرا می شود) بنابراین می توانیم state خود را نیز در آن تعریف کنیم. در حال حاضر state را پایین تر و خارج از constructor تعریف کرده ایم اما می توانیم آن را به درون constructor منتقل کنیم:

class App extends Component {
  constructor(props) {
    super(props);
    console.log('[App.js] constructor');
    this.state = {
      persons: [
        { id: 'asfa1', name: 'Max', age: 28 },
        { id: 'vasdf1', name: 'Manu', age: 29 },
        { id: 'asdf11', name: 'Stephanie', age: 26 }
      ],
      otherState: 'some other value',
      showPersons: false
    }
  }

در حالت قبلی از این حالت استفاده می کردیم:

  state = {
    persons: [
      { id: 'asfa1', name: 'Max', age: 28 },
      { id: 'vasdf1', name: 'Manu', age: 29 },
      { id: 'asdf11', name: 'Stephanie', age: 26 }
    ],
    otherState: 'some other value',
    showPersons: false
  }

این syntax یک روش جدیدتر برای نوشتن state بود اما در پشت صحنه کاری که انجام می دهد همان کاری است که ما انجام داده ایم؛ یعنی state به constructor برده می شود، super صدا زده می شود و state تعریف می شود. از آنجایی که این روش جدیدتری است ما به همین روش عمل می کنیم و آن را در constructor نمی آوریم (به سلیقه شما بستگی دارد).

سپس یاد گرفتیم که بعد از اجرای constructor یک متد به نام getDerivedStateFromProps اجرا می شود. از آنجایی که این متد یک متد static است باید کلیدواژه static را نیز قبل از آن بیاوریم:

  static getDerivedStateFromProps(props, state) {
    console.log('[App.js] getDerivedStateFromProps', props);
    return state;
  }

با اجرای این متد (برای دریافت state از props به صورت صحیح) متد render صدا زده می شود. بنابراین به قسمت انتهایی App.js و درون متد render بروید و بنویسید:

  render() {
    console.log('[App.js] render');
    let persons = null;
    if (this.state.showPersons) {
      persons = <Persons
        persons={this.state.persons}
        clicked={this.deletePersonHandler}
        changed={this.nameChangedHandler} />
    }

در جلسه قبل گفتم که هرگاه render صدا زده شود، تمام کامپوننت های فرزند (درون آن) نیز render می شود اما از آنجایی که Persons یک کامپوننت کاربردی است به Lifecycle ها دسترسی ندارد و نمی توانیم چیزی برایش بنویسیم. اما برای درک بهتر روند اجرای برنامه می توانیم به فایل Persons.js برویم و کد زیر را به آن اضافه کنیم:

const persons = (props) => {
    console.log('[Persons.js] rendering...');

    return props.persons.map((person, index) => {
        return (
            <Person
                click={() => props.clicked(index)}
                name={person.name}
                age={person.age}
                key={person.id}
                changed={(event) => props.changed(event, person.id)} />
        );
    });
}

اتفاق خاصی در این کد نیفتاده است؛ می خواستیم دستور console.log را درون تابع persons قرار دهیم بنابراین باید آن را از حالت خلاصه در می‌آوردیم. چرا؟ به دلیل اینکه فقط زمانی می توانیم از حالت خلاصه توابع ES6 استفاده کنیم که تنها چیزی را return کنیم. اگر قرار باشد دستورات جاوا اسکریپت و محاسباتی را نیز به آن اضافه کنیم باید کلیدواژه return را بیاوریم.

به فایل Person.js می رویم و همین کار را برای آن هم انجام می دهیم:

const person = (props) => {
    console.log('[Person.js rendering...');
    return (
        <div className={classes.Person}>
            <p onClick={props.click}>I'm {props.name} and I am {props.age} years old!</p>
            <p>{props.children}</p>
            <input type="text" onChange={props.changed} value={props.name} />
        </div>
    )
};

پس از render شدن تمام این موارد (Persons و تک تک Person ها) متد componentDidMount اجرا می شود بنابراین در فایل App.js و پایین تر از getDerivedStateFromProps می نویسیم:

  static getDerivedStateFromProps(props, state) {
    console.log('[App.js] getDerivedStateFromProps', props);
    return state;
  }

  componentDidMount() {
    console.log('[App.js] componentDidMount');
  }

در قسمت componentDidMount می توان درخواست های HTTP و عملیات های دیگری را نیز ایجاد کرد اما فعلا همین مقدار را نمایش می دهیم.

حالا اگر به قسمت console در dev tools مرورگر خود بروید (کلید f12) می بینید که تمامی log های ما به جز Persons و Person نمایش داده می شوند:

نمایش log ها به جز Person و Persons
نمایش log ها به جز Person و Persons

به نظر شما چرا؟

به دلیل اینکه این موارد هنوز render نشده اند! اگر روی دکمه Toggle Persons کلیک کنید این قسمت ها نیز اضافه می شوند:

نمایش تمامی log ها پس از render شدن کامل صفحه
نمایش تمامی log ها پس از render شدن کامل صفحه

همانطور که در تصویر بالا می بینید برای Render شدن این قسمت، متد getDerivedStateFromProps دوباره اجرا شده است (تا state را بگیرد) سپس App.js دوباره render شده است و Person.js نیز سه بار پیام console.log را نمایش داده است (به دلیل اینکه سه نفر یا سه Person برای render شدن داشتیم).

البته باید نکته ای را نیز به شما بگویم: lifecycle hook های دیگری در این قسمت (creation) وجود دارند اما به دلیل اینکه به اشتباه استفاده می شوند در آینده حذف خواهند شد بنابراین در موردشان صحبت نمی کنیم. به طور مثال componentWillMount یکی از این متدها است که دقیقا قبل از اجرای componentDidMount اجرا می شود و شما می توانید کارهایی مثل آماده سازی state را در آن انجام دهید که در حال حاضر در getDerivedStateFromProps انجام می شود.

امیدوارم درک خوبی نسبت به نحوه ساخت یک کامپوننت و render شدن آن به دست آورده باشید. با اینکه این جلسات، جلسات کدنویسی نیستند اما به نظر بنده تا این جزئیات را یاد نگرفته اید سراغ کدنویسی های بعدی نروید. درک کامل از محیطی که در آن کار می کنید همیشه بر کار کردن در آن محیط اولویت دارد!

دانلود کدهای این جلسه

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.