پاسخ و حل تمرینات فصل دوم دوره‌ی React.js

Answering and Solving Exercises for the Second Chapter of React.js

23 بهمن 1399
پاسخ و حل تمرینات فصل دوم دوره ی React.js

حل تمرینات فصل دوم

اگر این سری آموزشی را دنبال کرده باشید، می دانید که فصل دوم از سری آموزشی react نیز به پایان رسیده است. در جلسه قبل به شما تمریناتی ارائه کردیم تا آن ها را حل کنید و به دانش کدنویسی خود بیفزایید. حالا نوبت حل این تمرینات است.

تمرینات ما شامل 6 مورد بودند:

  1. در کامپوننت App یک input بسازید که رویدادی از نوع change داشته باشد. سپس طول (تعداد کاراکتر) رشته وارد شده در input را در پایین آن نمایش دهید (مثلا در یک عنصر p).
  2. یک کامپوننت جدید بسازید (به نام ValidationComponent) که طول رشته وارد شده در input را به صورت یک prop دریافت کند.
  3. درون کامپوننت ValidationComponent کدی بنویسید که اگر تعداد کاراکترها کمتر از 5 بود پیام «text too short» نمایش داده شود. در غیر این صورت پیام «text long enough» به معنی «اندازه متن مناسب است» نمایش داده شود.
  4. یک کامپوننت دیگر به نام CharComponent ایجاد کنید و آن را به شکل inline استایل دهی کنید (یعنی مقدار display را روی inline-block قرار داده، padding و margin را روی 16 پیکسل تنظیم کنید، text-align را روی center بگذارید و border را روی 1px solid black قرار دهید)
  5. لیستی از CharComponent را نمایش دهید به طوری که هر کدام از CharComponent ها یک حرف از حروف وارد شده در input را به عنوان prop دریافت کنند.
  6. کاری کنید که با کلیک روی CharComponent ها، این کامپوننت ها حذف شوند.

ما با قالب خالی زیر شروع می کنیم:

import React, { Component } from 'react';
import './App.css';

class App extends Component {

  render() {

    return (
      <div className="App">
        <ol>
          <li>Create an input field (in App component) with a change listener which outputs the length of the entered text below it (e.g. in a paragraph).</li>
          <li>Create a new component (=> ValidationComponent) which receives the text length as a prop</li>
          <li>Inside the ValidationComponent, either output "Text too short" or "Text long enough" depending on the text length (e.g. take 5 as a minimum length)</li>
          <li>Create another component (=> CharComponent) and style it as an inline box (=> display: inline-block, padding: 16px, text-align: center, margin: 16px, border: 1px solid black).</li>
          <li>Render a list of CharComponents where each CharComponent receives a different letter of the entered text (in the initial input field) as a prop.</li>
          <li>When you click a CharComponent, it should be removed from the entered text.</li>
        </ol>
        <p>Hint: Keep in mind that JavaScript strings are basically arrays!</p>
        <hr />
        
      </div>
    );
  }
}

export default App;

متن تمرینات را دست نمی زنم تا جلوی چشمم باشد و بعد از تگ hr کدنویسی می کنم اما اگر شما دوست دارید می توانید این لیست را حذف کنید.

برای تمرین اول باید یک input بسازیم که متن ورودی در آن، پایین تر از خودش نمایش داده شود بنابراین ابتدا یک input در کدهای JSX ایجاد می کنیم:

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

حالا متدی را تعریف می کنیم تا به onChange بدهیم. این متد درون کامپوننت App قرار بگیرد (خارج از render) اما این نوع از متدها state را تغییر می دهند بنابراین به state نیاز داریم:

  state = {
    userInput: ''
  }

فعلا مقداری برای state نداریم بنابراین آن را به شکل یک رشته خالی قرار می دهیم. شما می‌ توانید به جای userInput هر نامی را که خواستید تعیین کنید. اکنون می توانیم متدمان را با نام دلخواه تعریف کنیم. من نام inputChangedHandler را انتخاب می کنم:

  inputChangedHandler = (event) => {
    this.setState({userInput: event.target.value});
  }

این متد مقدار userInput (در state) را برابر با مقدار تایپ شده در input می کند. در جلسات قبلی به صورت مفصل در مورد آن صحبت کردیم.

حالا به input در JSX برمی گردیم و این متد را به آن پاس می دهیم:

<input type="text" onChange={this.inputChangedHandler} value={this.state.userInput} />

من مقدار value را نیز روی مقدار state تنظیم کرده ام تا همیشه در هنگام refresh صفحه این مقدار را نمایش دهد.

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

<input type="text" onChange={this.inputChangedHandler} value={this.state.userInput} />
<p>{this.state.userInput}</p>

بدین صورت متن تایپ شده را درون تگ p قرار می دهیم.

تمرین بعدی این بود که کامپوننتی بسازیم تا طول رشته (تعداد کاراکتر ها) را دریافت کند. بنابراین یک پوشه جدید به نام Validation می سازیم و در آن فایل Validation.js را می سازیم. محتویات این فایل بدین صورت است:

import React from 'react';

const Validation = (props) => {

};

export default Validation;

همانطور که می بینید از کامپوننت های کاربردی استفاده کرده ایم. برای اینکه خطایی دریافت نکنیم فعلا با یک تگ p مقداری JSX را برمی گردانیم و بعدا کدهای این قسمت را می نویسیم:

import React from 'react';

const Validation = (props) => {
    return (
        <div>
            <p>Text too short!</p>
        </div>
    );
};

export default Validation;

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

import Validation from './Validation/Validation'

تمرین به ما گفته بود که تعداد کاراکترهای متن وارد شده را به عنوان prop به این کامپوننت بدهیم بنابراین Validation را وارد JSX کرده و به راحتی یک prop به نام دلخواه برایش می نویسیم. من این نام را inputLength گذاشته ام:

<input type="text" onChange={this.inputChangedHandler} value={this.state.userInput} />
<p>{this.state.userInput}</p>
<Validation inputLength={this.state.userInput.length} />

با استفاده از یکی از متدهای خود جاوا اسکریپت به نام length می توانیم تعداد کاراکترهای یک رشته را محاسبه کنیم. حالا می توانیم درون Validation.js از آن استفاده کنید. تمرین بعدی می گوید طول رشته را محاسبه کنیم و بر اساس آن پیام text too short  یا text long enough را نمایش دهیم. بنابراین به Validation.js می رویم؛ دو راه برای حل این تمرین وجود دارد:

راه اول استفاده از ternary operator است:

const Validation = (props) => {
    return (
        <div>
            {
                props.inputLength > 5 ?
                    <p>Text long enough</p> :
                    <p>Text too short!</p>
            }
        </div>
    );
};

در این روش گفته ایم اگر تعداد کاراکترها بیشتر از 5 تا بود پیام اول و در غیر این صورت (علامت دو نقطه) پیام دوم را نمایش بده. اگر به مرورگر برویم متوجه می شویم که به خوبی کار می کند اما قبلا گفته بودیم که بهتر است در پروژه های واقعی از این روش استفاده نکنید چرا که کدهایتان را بسیار شلوغ می کند.

روش دوم استفاده از بلوک if است:

const Validation = (props) => {
    let validationMessage = 'Text long enough';
    if (props.inputLength <= 5) {
        validationMessage = 'Text too short!';
    }
    return (
        <div>
            <p>{validationMessage}</p>
        </div>
    );
};

در مورد هر دو روش به طور مفصل صحبت کرده ایم بنابراین توضیحات تکراری نمی دهم.

حالا نوبت تمرین بعدی است؛ باید کامپوننت Char را بسازیم و با توجه به موارد گفته شده در متن تمرین آن را استایل دهی کنیم. بنابراین پوشه (Char) و فایل (Char.js) مربوط به آن را ایجاد کنید:

import React from 'react';

const Char = () => {

}

export default Char;

برای استایل دهی این کامپوننت از همان دو روش معمولا می توانیم استفاده کنیم. من به جای ساختن فایل css از استایل های inline استفاده می کنم:

const Char = () => {

    const style = {
        display: 'inline-block',
        padding: '16px',
        margin: '16px',
        border: '1px solid black',
        textAlign: 'center'
    }

    return (
        <div style={style}>

        </div>
    );
}

حالا که این کامپوننت آماده شده است می توانیم آن را وارد App.js کنیم:

import Char from './Char/Char';

تمرین بعدی این است که هر کدام از کاراکترهایی را که کاربر وارد input کرده است به یکی از Char های متعدد برسد (لیست مانند). برای شروع، درون فایل App.js و داخل تابع render متغیری به شکل زیر تعریف می کنیم:

const charList = this.state.userInput.map();

سوال: چطور می توانیم از map روی یک رشته استفاده کنیم؟ مگر map برای آرایه ها نبود؟

پاسخ: بله map روی آرایه ها کار می کند و حرف شما درست است. برای تبدیل رشته خود به آرایه باید از متد split استفاده کنیم و یک رشته خالی را به عنوان پارامتر به آن پاس بدهیم.

بنابراین:

const charList = this.state.userInput.split('').map(ch => {
return <Char character={ch} />
});

یادآوری: map به آرایه اصلی دست نمی زند، بلکه یک آرایه جدید می سازد و آن را برمی گرداند. بنابراین charList یک آرایه جدید از عناصر است.

دقیقا مانند یک آرایه، ch نماینده هر کاراکتر است. حالا می توانیم به Char.js رفته و از آن به صورت props استفاده کنیم:

const Char = (props) => {

    const style = {
        display: 'inline-block',
        padding: '16px',
        margin: '16px',
        border: '1px solid black',
        textAlign: 'center'
    }

    return (
        <div style={style}>
            {props.character}
        </div>
    );
}

حالا می توانیم در App.js و پایین تر عنصر <Validation /> این آرایه را نمایش دهیم:

<Validation inputLength={this.state.userInput.length} />
{charList}
      </div>

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

از آنجایی که برنامه ما از پایگاه داده استفاده نمی کند و چیز دیگری هم در اختیار نداریم باید از همان index آرایه برای key استفاده کنیم. بنابراین:

const charList = this.state.userInput.split('').map((ch, index) => {
  return <Char character={ch} key={index} />
});

حالا با تست کردن کدها در مرورگر به هیچ خطایی برنمی خوریم.

تست برنامه
تست برنامه

برای تمرین بعد باید کاری کنیم که با کلیک روی هر کاراکتر، همان کاراکتر حذف شود. برای شروع، در فایل App.js و پایین تر از متد inputChangedHandler یک متد دیگر به نام deleteCharHandler می سازیم:

 deleteCharHandler = (index) => {
    
 }

Index همان index کاراکتر ما در آرایه ای است که جدیدا ساختیم. از آنجایی که این اتفاق باید زمانی روی بدهد که روی یکی از کاراکترها کلیک می شود به یک event کلیک نیاز داریم بنابراین به لیست <Char /> ها می رویم و این Event را اضافه می کنیم:

const charList = this.state.userInput.split('').map((ch, index) => {
      return <Char character={ch} key={index} clicked={() => this.deleteCharHandler(index)} />
    });

می توانیم برای خواناتر شدن کد آن را بشکنیم:

const charList = this.state.userInput.split('').map((ch, index) => {
      return <Char
        character={ch}
        key={index}
        clicked={() => this.deleteCharHandler(index)} />
    });

تفاوتی ندارد.

حالا به Char.js می رویم و قسمت JSX را تغییر می دهیم:

return (
        <div style={style} onClick={props.clicked}>
            {props.character}
        </div>
);

با این کار clicked را به آن پاس داده ایم.

حالا باید متد deleteCharHandler را بنویسیم. از آنجایی که متن وارد شده در input آرایه نیست باید آن را تبدیل به یک آرایه کنیم که با همان متد split قابل انجام است:

deleteCharHandler = (index) => {
    const text = this.state.userInput.split('');

}

باید یکی از آن ها را با متد splice حذف کنیم:

deleteCharHandler = (index) => {
    const text = this.state.userInput.split('');
    text.splice(index, 1);
}

در حال حاضر نوشته داخل input یک آرایه است اما ما می خواهیم یک متن را وارد userInput کنیم بنابراین با استفاده از تابع Join و پاس دادن یک رشته خالی به عنوان پارامتر به آن می توانیم آرایه کاراکتر ها را دوباره به یک رشته تبدیل کنیم و آن را به state بدهیم:

deleteCharHandler = (index) => {
    const text = this.state.userInput.split('');
    text.splice(index, 1);
    const updatedText = text.join('');
    this.setState({ userInput: updatedText });
}

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

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

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

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