تکمیل کدهای کامپوننت Cockpit.js

Complete the Cockpit.js Component Code

23 بهمن 1399
28-React-The-Complete-Guide

در قسمت قبل برای ایجاد لیست افراد یک کامپوننت جداگانه به نام Persons ایجاد کردیم و ساختار پوشه های پروژه را به طور کل تغییر دادیم. با این کار تمام پروژه ساختار یافته تر شده و کدهای ما حرفه ای به نظر می رسد.

حالا نوبت کدنویسی قسمت Cockpit رسیده است. منظور ما از Cockpit کد زیر در فایل App.js است:

return (
      <div className={classes.App}>
        <h1>Hi, I'm a React App</h1>
        <p className={assignedClasses.join(' ')}>This is really working!</p>
        <button
          className={btnClass}
          onClick={this.togglePersonsHandler}>Toggle Persons</button>
        {persons}
      </div>
);

برای شروع فایل Cockpit.js را باز کنید و کامپوننت خود را بنویسید:

const cockpit = () => {
    
}

نکته: در جلسه قبل از حالت خلاصه شده برای کامپوننت Persons استفاده کردیم اما در این جلسه به دلیل وجود استایل های دکمه و دیگر کدهای جاوا اسکریپت نمی توانیم این کار را انجام بدهیم (فقط چیزی را return نمی کنیم).

import React from 'react';

const cockpit = (props) => {
    return ();
}

export default cockpit;

حالا کد موردنظر را از فایل App.js برمی داریم و در این کامپوننت قرار می دهیم:

const cockpit = (props) => {
    return (
        <h1>Hi, I'm a React App</h1>
        <p className={assignedClasses.join(' ')}>This is really working!</p>
        <button
            className={btnClass}
            onClick={this.togglePersonsHandler}>Toggle Persons</button>
    );
}

این کد یک خطا دارد، آیا می توانید خطا را تشخیص دهید؟ خطا این است که تمامی کدهای JSX باید یک عنصر ریشه ای داشته باشند بنابراین باید تمام کدهای JSX را درون یک div قرار دهیم:

const cockpit = (props) => {
    return (
        <div>
            <h1>Hi, I'm a React App</h1>
            <p className={assignedClasses.join(' ')}>This is really working!</p>
            <button
                className={btnClass}
                onClick={this.togglePersonsHandler}>Toggle Persons
            </button>
        </div>
    );
}

حالا که کدهای موردنظر را از فایل App.js برداشتیم (cut کردیم) در قسمت JSX این فایل (App.js) باید چنین کدی را ببینیم.

    return (
      <div className={classes.App}>
        {persons}
      </div>
    );

نکته: نسخه 16 کتابخانه react راه هایی برای دور زدن این عنصر ریشه ای معرفی کرده است که بعدا در این دوره با آن آشنا می شویم. فعلا تمام کدهای JSX را درون یک عنصر ریشه ای قرار دهید.

به طور مثال اگر دقت کنید در فایل Persons.js با استفاده از تابع map یک لیست از افراد را Return کرده ایم:

const persons = (props) => 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)} />
})

این کد چند شیء Person را بدون وجود عنصر ریشه ای در کنار هم قرار می دهد. دلیل خطا نگرفتن react این است که به جای کدنویسی دستی عناصر از یک لیست استفاده کرده ایم. در واقع این راه یکی از راه های دور زدن این عنصر ریشه ای است.

حالا دوباره به کامپوننت cockpit برگردید. درون این کامپوننت دو متغیر به نام های assignedClasses و btnClass را داریم که قرار است کلاس های CSS را تعیین کنند. این کدها را نیز از فایل App.js برداشته و درون همین کامپوننت قرار دهید:

const cockpit = (props) => {
    const assignedClasses = [];
    if (this.state.persons.length <= 2) {
        assignedClasses.push(classes.red); // classes = ['red']
    }
    if (this.state.persons.length <= 1) {
        assignedClasses.push(classes.bold); // classes = ['red', 'bold']
    }
    
    return (
        <div>
            <h1>Hi, I'm a React App</h1>
            <p className={assignedClasses.join(' ')}>This is really working!</p>
            <button
                className={btnClass}
                onClick={this.togglePersonsHandler}>Toggle Persons
            </button>
        </div>
    );
}

ما در این کدها به state دسترسی داشته ایم اما حالا که آن ها را درون کامپوننت cockpit قرار داده ایم دیگر چنین دسترسی نداریم. بنابراین باید آن را تغییر دهیم:

    if (props.persons.length <= 2) {
        assignedClasses.push(classes.red); // classes = ['red']
    }
    if (props.persons.length <= 1) {
        assignedClasses.push(classes.bold); // classes = ['red', 'bold']
    }

بله می توانیم به صورت props به آن دسترسی داشته باشیم.

همچنین باید classes را نیز import کنیم بنابراین در پوشه Cockpit یک فایل به نام Cockpit.css ایجاد کنید. سپس کلاس های red و bold و کلاس های مربوط به دکمه برنامه (کلاس هایی که مورد استفاده cockpit است) را برمی داریم و در فایل Cockpit.css قرار می دهیم:

.red {
    color: red;
}

.bold {
    font-weight: bold;
}

.App button {
    border: 1px solid blue;
    padding: 16px;
    background-color: green;
    font: inherit;
    color: white;
    cursor: pointer;
}

.App button:hover {
    background-color: lightgreen;
    color: black;
}

.App button.Red {
    background-color: red;
}

.App button.Red:hover {
    background-color: salmon;
    color: black;
}

اما دیگر کلاس App را نداریم بنابراین کلاس App را به کلاس دلخواه خود تغییر دهید (بعدا باید آن کلاس را تعریف کنیم). من کلاس Cockpit را انتخاب می کنم:

.red {
    color: red;
}

.bold {
    font-weight: bold;
}

.Cockpit button {
    border: 1px solid blue;
    padding: 16px;
    background-color: green;
    font: inherit;
    color: white;
    cursor: pointer;
}

.Cockpit button:hover {
    background-color: lightgreen;
    color: black;
}

.Cockpit button.Red {
    background-color: red;
}

.Cockpit button.Red:hover {
    background-color: salmon;
    color: black;
}

در آخر آن ها را در فایل Cockpit.js وارد می کنیم:

import classes from './Cockpit.css';

حالا روی عنصر ریشه ای div کلاس Cockpit را اعمال می کنیم:

    return (
        <div className={classes.Cockpit}>
            <h1>Hi, I'm a React App</h1>
            <p className={assignedClasses.join(' ')}>This is really working!</p>
            <button
                className={btnClass}
                onClick={this.togglePersonsHandler}>Toggle Persons
            </button>
        </div>
    );

هنوز یک مورد دیگر باقی مانده است: btnClass

ما هنوز برای btnClass مقداری نداریم. به فایل App.js بروید و تعریف btnClass و همچنین تغییر آن در شرط if را حذف کنید.

تعریف آن:

let btnClass = '';

تغییر آن در if:

btnClass = classes.Red;

حالا که این موارد را از App.js حذف کردیم، باید وارد Cockpit.js کنیم:

const cockpit = (props) => {
    const assignedClasses = [];
    let btnClass = '';
    btnClass = classes.Red;

حالا باید با یک شرط تعیین کنیم که چه زمانی کلاس red به عنصر اضافه شود:

const cockpit = (props) => {
    const assignedClasses = [];
    let btnClass = '';
    if (props.showPersons) {
        btnClass = classes.Red;
    }

بله، مقدار showPersons درون state در فایل App.js قرار دارد بنابراین می توانیم به عنوان یک prop به آن دسترسی داشته باشیم اما هنوز چنین prop ای را ننوشته ایم. برای نوشتن prop های موردنظر به فایل App.js میرویم تا کامپوننت Cockpit را در آن پیاده سازی کنیم.

ابتدا Cockpit را import می کنیم:

import Cockpit from '../components/Cockpit/Cockpit';

سپس به قسمت JSX و return در فایل App.js میرویم و Cockpit را به همراه prop هایش تعریف می کنیم:

    return (
      <div className={classes.App}>
        <Cockpit
          showPersons={this.state.showPersons}
          persons={this.state.persons} />
        {persons}
      </div>
    );

حالا برای تمیزتر کردن کدهای App.js می توانیم عنصر ریشه ای div را از شرط if حذف کنیم:

  render() {
    let persons = null;
    if (this.state.showPersons) {
      persons = <Persons
        persons={this.state.persons}
        clicked={this.deletePersonHandler}
        changed={this.nameChangedHandler} />
    }

<Persons> یک کامپوننت است بنابراین نیازی به پرانتزها نبود.

حالا اگر به فایل App.js نگاه کنید متوجه می شوید که چقدر تمیز تر شده است. کدهای JSX آن کم شده و تنها متد هایی دارد که state را تغییر می دهند. کار کامپوننت های container نیز همین است؛ مدیریت state به همراه حداقل کدهای JSX.

اگر به مرورگر بروید و برنامه را تست کنید متوجه می شوید که دکمه ما کار نمی کند. می دانید چرا؟ به فایل Cockpit.js بروید و عنصر دکمه را نگاه کنید:

<button
        className={btnClass}
        onClick={this.togglePersonsHandler}>Toggle Persons
</button>

ما در حال حاضر در یک کامپوننت کاربردی هستیم بنابراین نمی توانیم از کلیدواژه this استفاده کنیم! راه حل دقیقا راه حل موارد دیگر است: استفاده از props!

<button
        className={btnClass}
        onClick={props.clicked}>Toggle Persons
</button>

حالا به فایل App.js برمی گردیم و در قسمت JSX این prop را تعریف می کنیم:

    return (
      <div className={classes.App}>
        <Cockpit
          showPersons={this.state.showPersons}
          persons={this.state.persons}
          clicked={this.togglePersonsHandler} />
        {persons}
      </div>
    );

حالا همه چیز مانند قبل کار می کند ولی پروژه ما بسیار تمیزتر شده است. شما باید تمام پروژه های React واقعی خود را به همین صورت انجام دهید.

نکات مهم این جلسه و جلسه قبل:

  • هر کامپوننت باید وظیفه مشخصی داشته باشد.
  • هر کامپوننت باید روی یک چیز تمرکز کند نه روی چند چیز.
  • کامپوننت ها باید ریز باشند بنابراین کامپوننت های بسیار بزرگ را بشکنید.
  • تا حد ممکن از کامپوننت های کاربردی (functional) استفاده کنید نه کلاس-محور.
  • کامپوننت های container را از نظر JSX تا حد ممکن کوچک و ساده نگه دارید.

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

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

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