انتقال متد بین کامپوننت‌ها به عنوان property

Transfering Method Between Components as Property

23 بهمن 1399
انتقال متد بین کامپوننت ها به عنوان property

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

کدهای بدون hook (کلاس-محور)

برنامه ما تا این قسمت یک دکمه دارد که با فشردن آن نام Max و سن Stephanie تغییر می کند. اگر بخواهیم این اتفاق با کلیک روی متن یکی از این عناصر اتفاق بیفتد چطور؟ می توانیم به فایل Person.js برویم و روی یکی از پاراگراف ها رویداد onClick را اضافه کنیم:

 const person = (props) => {
    return (
        <div>
            <p onClick={}>I'm {props.name} person and I am {props.age} years old!</p>
            <p>{props.children}</p>
        </div>
    )
}

اما نمی توانیم switchNameHandler را صدا بزنیم چرا که درون یک فایل دیگر است. به نظر شما راه حل چیست؟

ما می توانیم یک property داشته باشیم که در اصل یک ارجاع به تابع switchNameHandler است! به طور مثال به قسمت JSX از فایل App.js نگاه کنید:

render() {
    return (
      <div className="App">
        <h1>Hi, I'm a React App</h1>
        <p>This is really working!</p>
        <button onClick={this.switchNameHandler}>Switch Name</button>
        <Person
          name={this.state.persons[0].name}
          age={this.state.persons[0].age}
        />
        <Person
          name={this.state.persons[1].name}
          age={this.state.persons[1].age}
        >
          My Hobbies: Racing
        </Person>
        <Person
          name={this.state.persons[2].name}
          age={this.state.persons[2].age}
        />
      </div>
    );
    // return React.createElement('div', {className: 'App'}, React.createElement('h1', null, 'Does this work now?'));
  }

می توانیم یک property دیگر (مانند name و age) تعریف کنیم اما به جای اشاره به مقداری خاص، به تابع switchNameHandler اشاره می کنیم:

<Person
          name={this.state.persons[1].name}
          age={this.state.persons[1].age}
          click={this.switchNameHandler}
        >
          My Hobbies: Racing
</Person>

من این property ها را در چند خط جداگانه گذاشته ام تا بهتر دیده شوند، شما می توانید این کار را نکنید. همانطور که می بینید نام ارجاع را click انتخاب کرده ام اما شما می توانید هر نام دیگری را برای این property انتخاب کنید. حالا می توانیم به فایل Person.js برویم و با استفاده از props به این property دسترسی داشته باشیم:

return (
        <div>
            <p onClick={props.click}>I'm {props.name} and I am {props.age} years old!</p>
            <p>{props.children}</p>
        </div>
    )

با باز کردن مرورگر و کلیک روی پاراگراف مورد نظر می بینیم که متن تغییر می کند. بنابراین متدها می توانند از طریق property ها منتقل شوند.

یک قدم جلوتر می رویم. اگر بخواهیم switchNameHandler یک نام جدید را بگیرد و نام Max را با آن جایگزین کند چطور؟

switchNameHandler = (newName) => {
    // console.log('Was clicked!');
    // DON'T DO THIS: this.state.persons[0].name = 'Maximilian';
    this.setState({
      persons: [
        { name: newName, age: 28 },
        { name: 'Manu', age: 29 },
        { name: 'Stephanie', age: 27 }
      ]
    });
  };

حالا این داده را چطور از طریق property ارسال کنیم؟ دو راه وجود دارد.

راه اول این است که از متد bind استفاده کنیم و کلیدواژه this را به آن بدهیم:

<button onClick={this.switchNameHandler.bind(this)}>Switch Name</button>

اینجا this یعنی همان this که داخل تابع switchNameHandler است! همین کافی است! بله، عجیب است اما در جاوا اسکریپت بدین صورت با this رفتار می شود (معضلات مشهور this در جاوا اسکریپت).

اما راه بهتر این است که یک آرگومان دیگر نیز اضافه کنیم که در واقع به صورت پارامتر به تابع ارسال می شود:

<button onClick={this.switchNameHandler.bind(this, 'Maximilian')}>Switch Name</button>

برای اینکه بهتر متوجه تغییر شویم این کار را روی یک پاراگراف هم انجام می دهیم:

<Person
          name={this.state.persons[1].name}
          age={this.state.persons[1].age}
          click={this.switchNameHandler.bind(this, 'Max!')}
        >
          My Hobbies: Racing
 </Person>

با اینکار بر اساس اینکه کجا کلیک کنیم، نتایج مختلفی می گیریم. حالا فایل را ذخیره کنید و آن را در مرورگر تست کنید؛ یک بار کلیک روی دکمه و بار دیگر کلیک روی پاراگراف مربوطه.

با کلیک روی دکمه، نام به Maximilian و با کلیک روی پاراگراف نام به Max! تغییر می کند که دقیقا همان چیزی است که ما می خواهیم.

روش دوم استفاده از یک arrow function است:

<button onClick={() => this.switchNameHandler('Maximilian!!')}>Switch Name</button>

در جلسات «مروری بر ES6» توضیح دادیم که زمانی که arrow function در یک خط نوشته شود، نیازی به نوشتن کلیدواژه return نیست بلکه return به صورت خودکار انجام می شود. بنابراین حواستان باشد که در کد بالا return را انجام داده ایم اما آن را به روش خلاصه (در یک خط) نوشته ایم.

روش دوم، روش سنگین تری است و ممکن است باعث شود که react عناصر صفحه را بیش از حد re-render کند بنابراین روش پیشنهادی ما همان روش اول است.

باز هم یک قدم جلوتر می رویم!

اگر بخواهیم مقدار نام را خود کاربر تغییر دهد چطور؟ برای این کار وارد فایل Person.js شوید و یک input عادی را اضافه کنید:

const person = (props) => {
    return (
        <div>
            <p onClick={props.click}>I'm {props.name} and I am {props.age} years old!</p>
            <p>{props.children}</p>
            <input type="text" />
        </div>
    )
};

برای دریافت کد وارد شده توسط کاربر می توانیم از رویداد onchange استفاده کنیم. بنابراین آن را اضافه می کنیم:

<input type="text" onChange={} />

حالا به فایل App.js رفته و یک handler دیگر به نام nameChangedHandler می سازیم:

nameChangedHandler = (event) => {
    this.setState({
      persons: [
        { name: newName, age: 28 },
        { name: 'Manu', age: 29 },
        { name: 'Stephanie', age: 27 }
      ]
    });
  };

event یک شیء است که رویدادها را دریافت می کند. همچنین از آنجایی که می خواهیم state را تغییر دهیم بنابراین دوباره state را درون این handler کپی کرده ایم.

در حالت عادی می خواهیم نام فردی تغییر کند که در input مربوطه اش چیزی می نویسیم اما هنوز به مبحث نمایش لیستی از عناصر پویا نرسیده ایم. بنابراین فعلا کد را طوری می نویسیم که هر زمان درون هر کدام از input ها چیزی تغییر کرد نام Manu تغییر کند:

nameChangedHandler = (event) => {
    this.setState({
      persons: [
        { name: 'Max', age: 28 },
        { name: event.target.value, age: 29 },
        { name: 'Stephanie', age: 26 }
      ]
    });
  };

در این کد ابتدا نام max را به صورت قبلی همان Max گذاشته ایم و سن Stephanie را نیز روی مقدار قبلی 26 تنظیم کرده ایم. می خواهیم Manu تغییر نام دهد، بنابراین شیء event را صدا میزنیم و target (هدف) آن را می گیریم (که می شود همان input ما، چرا که هدف رویداد onChange همان input بود). در آخر با استفاده از value مقدار تایپ شده درون آن را دریافت می کنیم.

حالا باید این تابع را به صورت یک property پاس دهیم و از آنجایی که فقط می توانیم نام Manu را تغییر دهیم آن را به عنوان یکی از خصوصیات Manu پاس می دهیم:

<Person
          name={this.state.persons[1].name}
          age={this.state.persons[1].age}
          click={this.switchNameHandler.bind(this, 'Max!')}
          changed={this.nameChangedHandler}
        >
          My Hobbies: Racing
</Person>

اکنون می توانیم به فایل Person.JS برگشته و آن را پاس بدهیم:

return (
        <div>
            <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} />
        </div>
    )

نیازی به نگرانی برای شیء event نیست. همانطور که می دانید در جاوا اسکریپت شیء event به صورت خودکار پاس داده می شود و به آن دسترسی داریم. حالا فایل ها را ذخیره می کنیم و به مرورگر سری میزنیم.

به تمام پاراگراف ها یک input اضافه شده است اما اگر درون input های اول و سوم چیزی بنویسیم، مقدار نام تغییر نمی کند چرا که برای آن ها کدی ننوشتیم و آن را فقط برای Manu تنظیم کردیم:

نتیجه ی input ها در صفحه ی مرورگر
نتیجه input ها در صفحه مرورگر

حالا اگر در Manu چیزی بنویسیم:

نوشتن هر مقداری در پاراگراف Manu باعث تغییر می شود
نوشتن هر مقداری در پاراگراف Manu باعث تغییر می شود

با هر بار فشردن کلیدی روی کیبورد مقدار نام نیز تغییر می کند (رویداد onChange).

اگر بخواهیم در زمان بارگذاری صفحه، مقدار اولیه نام در input نمایش داده شود می گوییم:

<input type="text" onChange={props.changed} value={props.name} />

با مراجعه به مرورگر می بینیند که با refresh شدن صفحه مقدار اولیه نام در input نمایش داده می شود مگر اینکه آن را تغییر دهیم.

امیدوارم از این قسمت لذت برده باشید.

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

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

امیر خدادادی
26 تیر 1400
لطفا اگر ممکنه توضیحی در مورد کلید واژه this که در bind به کار برده شده توضیح بفرمایید. My Hobbies: Racing

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