توسعه یک اپلیکیشن هواشناسی با React.js - بخش اول

توسعه یک اپلیکیشن هواشناسی با React.js - بخش اول
React، از محبوب‌ترین کتابخانه‌های جاوااسکریپت می‌باشد که شرکت‌های بزرگ و کوچک مانند اینستاگرام، نتفلیکس، Airbnb و بسیاری دیگر، از کتابخانه استفاده می‌کنند.
آموزش برنامه نویسی موبایل
آموزش برنامه نویسی اندروید به همراه پشتیبانی رایگان برای رفع خطاهای شما
خودتان را اینجا معرفی کنید

چرا React.js؟

React به قول فیس‌بوک، برای حل یک مشکل ساخته شده بود: «ساختن برنامه‌های کاربردی بزرگ، با داده‌هایی که در طول زمان متغیر هستند.» (که دقیقا به‌همین علت، اینستاگرام و فیس‌بوک با استفاده از React ساخته شده‌اند)
با استفاده از React، می‌توان نشان داد که برنامه‌ی کاربردی شما در هر زمان، به چه شکلی باشد. زمانی که داده‌ها تغییر می‌کنند، React تشخیص می‌دهد که کدام قسمت‌های برنامه، نیاز به بروزرسانی دارند و تنها آن قسمت‌ها رندر می‌کند.
یکی از دلایل محبوبیت React، قابلیت‌های آن برای ساختن برنامه های کاربردی است که encapsulated و قابل استفاده‌ی مجدد هستند و می‌توان برایشان کامپوننت‌های مختلفی نوشت.

آن‌چه ما خواهیم ساخت:

برنامه‌ای که در زیر مشاهده می‌کنید، برنامه‌ای کاربردی است که در این مجموعه‌ی آموزشی خواهیم ساخت:



مثال فوق را می‌توانید در مرورگر خود، از طریق لینک زیر مشاهده نمایید:
blog.mxstbr.com/weather-app

در اولین فصل این خودآموز، ابتدا با مفاهیم React آشنا می‌شویم و در فصل دوم، ساختن برنامه‌ی کاربردی فوق را شروع می‌کنیم. پیشنهاد می‌کنیم حتی اگر با مفاهیم React آشنایی دارید، این فصل را حتما مطالعه کنید.

شروع 

برای توضیحات اولیه، از react.jsbin.com استفاده خواهیم کرد که محیطی با تمام امکانات، برای استفاده از React می‌باشد.
ریئکت شامل دو کتاب‌خانه‌ی React  و ReactDOM می‌باشد. 
با استفاده از کتاب‌خانه‌ی React، می‌توان المنت‌ها یا موجودیت‌های مختلف را ایجاد کرد؛ که این المنت‌ها، توسط ReactDOM رندر می‌شوند. علت جدا بودن این دو کتاب‌خانه، این است که می‌توان المنت‌های ریئکت را هرجایی و نه فقط در DOM مرورگر، رندر کرد.
برای مثال، آزمایش‌های اولیه‌ای برای رندر کردن React در Canvas، WebVR و حتی در سخت‌افزار وجود دارد!
با بازکردن این لینک تگ <h1> با متن "Hello World" را مشاهده خواهید کرد، که سورس کد زیر این متن را ایجاد می‌کند:
ReactDOM.render(
  React.createElement('h1', {className: 'heading'}, 'Hello World'),
  document.getElementById('container')
);

از تابع ReactDOM.render()، برای رندرکردنReactElement ‌ای که با استفاده از تابعReact.createElement()  ایجاد شده است، استفاده می‌کنیم.

ReactDOM.render()

ReactDOM.render()، تابعی است که دو آرگومان نیاز دارد: یکی همان ReactElementای‌ست که رندر خواهد شد و دیگری گره‌ی DOMای که می‌خواهیم داخل آن رندر کنیم.
 


ReactDOM.render(
  React.createElement('h1', {className: 'heading'}, 'Hello World'),
  document.getElementById('container')
);

React.createElement()

این تابع، node یا ReactElementای را که می‌خواهیم ایجاد کنیم، به‌عنوان اولین آرگومان و برخی از خواص (مانند type) یک شیء را، به‌عنوان دومین آرگومان می‌گیرد:
 


React.createElement('input');
// -> <input></input>
React.createElement('input', { type: 'radio' });
// -> <input type="radio"></input>
React.createElement('input', { className: 'heading', type: 'radio' });
// -> <input class="heading" type="radio"></input>


به این نکته توجه کنید که ویژگی class در HTML با خاصیتclassName  در react نوشته می‌شود. این کار به این علت است که class یک کلمه‌ی کلیدی ذخیره شده در جاوااسکریپت است، که احتمال دارد ما یک مشکل ناخواسته را با فرستادنclass  وارد برنامه‌ی خود بکنیم. بنابرین react برای جلوگیری از این اشتباه، به‌جای class از className استفاده می‌کند.
هم‌چنین می‌توانیم فرزندان یک گره (node) را، به‌عنوان سومین آرگومان به تابع بدهیم. در مثالی که مشاهده می‌کنید، ""Hello World که فرزند گره‌ی h1 و یک متن است، به‌عنوان سومین آرگومان به تابع داده شده است:
 


React.createElement('h1', null, 'Hello World');
// -> <h1>Hello World</h1>


فرزندان (در مثال فوق: ""Hello World) می‌توانند ReactElement دیگری باشند!
فرض کنیم که می‌خواهیم یک <div class=”wrapper”> اطرافheading  اضافه کنیم؛ برای این کار از React.createElement برای رندرکردن یکdiv  همراه classNameای به نام "wrapper" استفاده می‌کنیم و سپس heading  را برای فرزند ReactElement به‌عنوان آرگومان می‌فرستیم. (به‌عبارتی خود المنت h1‌ای که از طریق react.createElement ایجاد می‌کنیم، به‌عنوان آرگومان سوم استفاده شده است)
 


React.createElement('div', { className: 'wrapper' },
  React.createElement('h1', null, 'Hello World')
)

کد فوق، HTMLای که در زیر مشاهده می‌کنید را رندر خواهد کرد:
 


<div class="wrapper">
  <h1>Hello World</h1>
</div>

<div class=”wrapper”> ممکن است max-width  و برخی style های دیگری داشته باشد. با استفاده‌ی مجدد از این المنت، می‌توان اطمینان حاصل کرد که برنامه‌ی کاربردی ما ثابت و بدون تناقض می‌باشد؛ چرا که این المنت هرجا که استفاده شود، همان styling را خواهد داشت. Componentها دقیقا به‌همین منظور استفاده می‌شوند.

Components

برای ایجاد کردن یک ReactComponent، تابعی می‌نویسیم که یک ReactElement را برمی‌گرداند.
 


var Wrapper = function(props) {
  return React.createElement('div', { className: 'wrapper' });
}

سپس می‌توانیم مانند گره‌های DOM، از این componentها به شکل زیر استفاده کنیم (به‌عنوان یک آرگومان، در فراخوانی تابع createElement استفاده کنیم):
 


React.createElement(Wrapper);
// -> <div class="wrapper"></div>

کامپوننت wrapper به‌علت اینکه فرزندان‌اش را رندر نمی‌کند، سودمند نیست و نتیجه‌ای نشان نمی‌دهد:
 


React.createElement(Wrapper, {}, 'Hello World!');
// -> <div class="wrapper"></div> (no "Hello World" visible!)


این امر به این علت است که تابع کامپوننت از propertyها می‌گذرد، بدون اینکه از آن‌ها استفاده کند. در مثال فوق ما ازproperty هایی که فراخوانیcreateElement(Wrapper)  می‌دهد، بدون استفاده رد شدیم. طبق کد زیر، فرزندان wrapper را رندر می‌کنیم:
 


var Wrapper = function(props) {
  // Render the children we get passed from the createElement(Wrapper) call
  return React.createElement('div', { className: 'wrapper' }, props.children);
}


React.createElement(Wrapper, {}, 'Hello World!');
// <div class="wrapper">Hello World</div>

می‌توانیم با کد زیر، heading خود را داخل کامپوننت wrapper رندر کنیم و درنهایت یک کامپوننت Wrapper با قابلیت استفاده‌ی مجدد داریم:
 


React.createElement(Wrapper, null,
  React.createElement('h1', null, 'Hello World')
);
// <div class="wrapper"><h1>Hello World</h1></div>

نمایش نتیجه

JSX

درصورتی که نمونه‌هایی از کدهای نوشته‌شده باReact  را دیده باشید، ممکن است متوجه سینتکس HTMLمانندی در کد جاوااسکریپتی که توسط بسیاری استفاده می‌شود، شده باشید.
این سینتکس  “JSX” نام دارد، که یک wrapper برای React.createElement می‌باشد.
به‌جای فراخوانی React.createElement به‌صورت دستی، می‌توان از JSX استفاده کرد تا کد، مانند یک  HTML رندرشده نمایش داده شود.
هر دو کد زیر، یکسان هستند:

 

<Wrapper>
  <h1 className="heading">Hello World</h1>
</Wrapper>


React.createElement(Wrapper, null,
  React.createElement('h1', {className: 'heading'}, 'Hello World')
)

استفاده از JSX ممکن است کمی دشوار باشد؛ چراکه یک افزونه‌ی (extension) غیر استاندارد از جاوااسکریپت می‌باشد، که هیچ مرورگری آن را متوجه نمی‌شود. درنتیجه لازم است تا کد خود را توسط یک build tool، transpile کنیم.
با کد زیر می‌توان کامپوننت Wrapper را تغییر داد، تا از JSX ا استفاده کرد:
 


var Wrapper = function(props) {
  return (
    <div className="wrapper">{ props.children }</div>
  );
};

نمایش نتیجه

این کار تفاوت چندانی با فراخوانی createElement به صورت دستی ندارد، اما JSX خواناتر است و راحت‌تر قابل درک می‌باشد؛ به‌همین دلیل برای نوشتن برنامه‌های react از JSX استفاده خواهیم کرد.

Classes

همان‌طور که در بخش «چرا React؟» گفته شد، React یک DOM مجازی دارد برای به حداقل رساندن رندرکردن‌ها، تا زمانی که وضعیت برنامه تغییر می‌کند. اما وضعیت برنامه چیست و چگونه آن را در React مدیریت می‌کنیم؟
هر برنامه‌ی کاربردی‌ای، وضعیت (state) خواهد داشت. State می‌تواند هرچیزی باشد.
به‌عنوان یک مثال ساده از state، کامپوننت‌شمارنده (counter) که تعداد دفعاتی که روی یک دکمه کلیک می‌کنیم را می‌شمارد، می‌سازیم. کامپوننت Wrapper در بالا، به‌عنوان یکfunctional component  نوشته شده است. برای ایجاد کردن یک stateful component، از کلمه‌ی کلیدی class استفاده می‌کنیم.
همان‌طور که در کد زیر مشاهده می‌کنید، برای ایجاد کردن یک کامپوننت stateful، یک class جدید ایجاد می‌کنیم که React.Component راextend  می‌کند, سپس یک متد render که ReactElements را برمی‌گرداند، برای کلاس خود تعیین می‌کنیم:
 


class Counter extends React.Component {
  render() {
    return (
      <p>This is the Counter component!</p>
    );
  }
}

این کامپوننت را مانند هر کامپوننت دیگری، توسط ReactDOM.render، رندر می‌کنیم:
 


ReactDOM.render(
  <Counter />,
  document.getElementById('container')
);

نمایش نتیجه

همان‌طور که در کد زیر مشاهده می‌کنید، می‌توانیم یک کامپوننت button جداگانه ایجاد کنیم که prop با نام text می‌گیرد. از آن‌جایی که نیازی نیست تا هیچ وضعیتی را این کامپوننت ذخیره کند، به‌عنوان یک کامپوننت کاربردی آن را می‌نویسیم.

 


var Button = function(props) {
  return (
    <button>{ props.text }</button>
  );
}

این دکمه (button) را داخل شمارنده‌ی خود، با یک متن "click me!" رندر می‌کنیم:
 


class Counter extends React.Component {
  render() {
    return (
      <div>
        <p>This is the Counter component!</p>
        <Button text="Click me!"/>
      </div>
    );
  }
}

نمایش نتیجه

اکنون با استفاده از onClick، هربار که کاربر روی دکمه‌ی button کلیک می‌کند، شمارنده یک واحد افزوده می‌شود:

 


class Counter extends React.Component {
  render() {
    return (
      <div>
        <p>This is the Counter component!</p>
        <Button text="Click me!" onClick={function() { console.log('click!') }} />
      </div>
    );
  }
}


در این‌جا لازم است تا بینreact component  و گره‌های DOM واقعی تفاوتی باشد. Event Handlerها مانند onClick، onMouseOver و...، تنها زمانی کار می‌کنند که همراه با گره‌های واقعی DOM باشند. مثال فوق کار نمی‌کند، زیرا فقط به ReactComponent متصل است و با کلیک کردن روی دکمه، عبارت "click!" را در کنسول مشاهده نمی‌کنید.
برای حل این مشکل، کنترلر onClick را در داخل کامپوننت button، به گره‌ی native DOM button متصل می‌کنیم:
 


var Button = function(props) {
  return (
    <button onClick={props.onClick} { props.text }</button>
  );
}

نمایش نتیجه

در واقعیت نمی‌خواهیم که با هربار کلیک کردن روی دکمه، عبارت "click!" را ثبت کنیم؛ هدف شمردن تعداد دفعاتی است که روی دکمه کلیک می‌شود. برای این کار به کامپوننت‌شمارنده‌ی خود،state  را اضافه می‌کنیم. State یک شئ ساده در react است، که می‌تواند هر تعداد properties داشته باشد. در مثال ما، state یک ویژگی با نام clicks با مقدار اولیه‌ی صفر خواهد داشت که این مقدار با هر کلیک، افزایش می‌یابد.
هر کلاسی دارای متد constructor می‌باشد که برای مقداردهی اولیه به‌صورت خودکار فراخوانی می‌شود. با استفاده از این متد، وضعیت اولیه‌ی کامپوننت‌شمارنده را تعیین می‌کنیم:
 


class Counter extends React.Component {
  constructor() {
    super();
    this.state = {
      clicks: 0
    };
  }

  render() { /* ... */ }
}

این کار به تنهایی تغییری ایجاد نمی‌کند و ما هم‌چنان شماره را در صفحه‌ی خروجی خود مشاهده نمی‌کنیم. برای دست‌یابی به وضعیت کنونی کامپوننت در هرجایی داخل کامپوننت، از this.state استفاده می‌کنیم:
 


class Counter extends React.Component {
  constructor() {
    super();
    this.state = {
      clicks: 0
    };
  }

  render() {
    return (
      <div>
        <p>This is the Counter component! The button was clicked { this.state.clicks } times.</p>
        <Button text="Click me!" onClick={function() { console.log('click!') }} />
      </div>
    );
  }
}


نمایش نتیجه

Counter ما اکنون به شکل زیر می‌باشد اما با کلیک‌ کردن روی دکمه، تعداد کلیک افزایش نمی‌یابد!



برای تغییر دادن وضعیت یک کامپوننت، از this.setState استفاده می‌کنیم که یک تابع کمکی است که توسط React فراهم شده است.
متد increment را به شمارنده‌ی Counter اضافه می‌کنیم که وضعیت clicks را یک‌واحد افزایش می‌دهد و this.increment را، زمانی که روی دکمه‌ی  Buttonکلیک می‌شود، فراخوانی می‌کند.

 


class Counter extends React.Component {
  constructor() {
    super();
    this.state = {
      clicks: 0
    };
  }

  increment() {
    this.setState({
      clicks: this.state.clicks + 1
    });
  };

  render() {
    return (
      <div>
        <p>This is the Counter component! The button was clicked { this.state.clicks } times.</p>
        <Button text="Click me!" onClick={this.increment} />
      </div>
    );
  }
}


مشکلی که در این قسمت پیش می‌آید این است که به خاطر عملکردی که classها در ES6 دارند، this در increment تعریف نشده است. آسان‌ترین راه‌حل برای این موضوع، bind کردن متن increment به کلاس در داخل constructor  به‌صورت زیر می‌باشد:
 


class Counter extends React.Component {
  constructor() {
    super();
    this.state = {
      clicks: 0
    };
    this.increment = this.increment.bind(this);
  }

  increment() {
    this.setState({
      clicks: this.state.clicks + 1
    });
  };

  render() {
    return (
      <div>
        <p>This is the Counter component! The button was clicked { this.state.clicks } times.</p>
        <Button text="Click me!" onClick={this.increment} />
      </div>
    );
  }
}


نمایش نتیجه

اکنون Counter به‌درستی کار می‌کند و با هربار کلیک کردن روی دکمه، مقدار شمارنده را یک‌واحد افزایش می‌دهد.
اکنون با مفاهیم React آشنا هستیم و هرچیزی را که برای ساختن اولین برنامه‌ی کاربردی خود لازم است، می‌دانیم. 
در ادامه می‌توانید وارد فصل دوم این مجموعه‌ی آموزشی خواهیم شد و نوشتن اولین برنامه را آغاز می‌کنیم.

منبع: plot.ly

فرناز عبداللهی هستم دانشجوی مهندسی فناوری اطلاعات، با شروع از html , css وارد حوزه ی طراحی وب شده ام و در حال حاضر در حال یادگیری جاوا اسکریپت می باشم.

نظرات و سوالات کاربران

ارسال پاسخ فرناز عبداللهی
فرناز عبداللهی
چهارشنبه ۲۴ خرداد ۱۳۹۶ ۰۱:۲۰
در پاسخ به دیدگاه Ali Zamani ارسال شده در شنبه ۲۳ اردیبهشت ۱۳۹۶ ۱۹:۰۵
سلام و ممنون . جالبه من هنوز علت استفاده از react رو دقیقا متوجه نمی شم . مثلا همین سایت http://mxstbr.blog/weather-app رو میشه با ajax و jquery نوشت !
این یک اپلیکیشن ساده هست و هدفمون آموزش react هست ولی react برای اپلیکیشن های بزرگ که کامپوننت های خیلی زیادی دارند کارایی خیلی بهتری نسبت به بقیه کتابخانه ها داره و به خاطر استفاده از virtual DOM سرعت بالایی داره. میشه باهاش کامپوننت هایی ایجاد کرد که به سادگی میشه به دفعات متعدد از این کامپوننت ها استفاده کرد.
در کل React و jquery رو درست نیست که باهم مقایسه کنیم چون که jquery برای بحث های انیمیشن و ... کاربرد داره اما react برای مدیریت view ، کامپوننت ها و بحث virtual DOM هست.
ارسال پاسخ Ali Zamani
Ali Zamani
شنبه ۲۳ اردیبهشت ۱۳۹۶ ۱۹:۰۵
سلام و ممنون . جالبه من هنوز علت استفاده از react رو دقیقا متوجه نمی شم . مثلا همین سایت mxstbr.blog/weather-app رو میشه با ajax و jquery نوشت !
ارسال پاسخ samuel
samuel
چهارشنبه ۱۳ اردیبهشت ۱۳۹۶ ۰۹:۱۳
great article. full of useful information. thanks for sharing