نظرات مطالب
خواندن اطلاعات از سرور و نمایش آن توسط Angular در ASP.NET MVC
با تشکر 
من از این روش در یک view استفاده کردم (در اینجا هدف نمایش لیست کاربران بود) ولی مشکل من این هست که وقتی این view برای کاربر نمایش داده میشه چون انگولار اون رو کش میکنه با افزودن کاربر جدید این لیست تا درخواست مجدد از سرور بروز نمیشه.
میخواستم بدونم آیا با واکشی اطلاعات توسط http$ مشکلم حل میشه؟
یا میشه یک view در انگولار کش نشود و به هر با route به اون view مورد نظر از سرور فراخوانی بشه
نظرات مطالب
تغییرات بوجود آمده در Bundling and Minification -MVC4
اینطور نیست. مطابق مستندات رسمی آن ، HTTP Expires Header به مدت یکسال بر روی منابع تولیدی تنظیم می‌شود (و مرورگر مرتبا درخواست دریافت این منابع را نمی‌دهد). اینجا است که نیاز به غیرمعتبر کردن کش نیز وجود خواهد داشت. بنابراین در انتهای لینک تولیدی اگر دقت کرده باشید css?v=something وجود دارد. این v به معنای نگارش است. اگر این نگارش که به صورت خودکار بر اساس هش فایل‌ها تولید می‌شود تغییر کند، مرورگر کش خود را به روز خواهد کرد.
ضمن اینکه در اینجا قرار نیست فایلی یا فایل‌هایی با همان نام‌های اصلی ارسال شوند. به این دلیل که هدف اصلی این کار bundling، کم کردن تعداد درخواست‌ها به سرور نیز هست. برای مثال اگر سایت شما از 8 فایل اسکریپت استفاده می‌کند، در اینجا تبدیل به یک فایل خواهد شد که سبب کم شدن تعداد درخواست‌ها و سریع‌تر شدن نمایش صفحات می‌شود.
نظرات مطالب
مروری بر کاربردهای Action و Func - قسمت دوم
در متن توضیح دادم «... الگوی تکراری زیر باید طی شود ...».
برای خواندن اطلاعات از کش سیستم، این الگوی تکراری در هرجایی از برنامه باید انجام شود:
الف) ابتدا باید به شیء Cache مراجعه شود. شاید بر اساس یک key مفروض، اطلاعاتی در آن موجود باشد.
ب) اگر نبود (قطعه if تعریف شده)، باید به یک منبع داده مشخص، مراجعه و اطلاعات دریافت شود. سپس این اطلاعات در کش برای دفعات مراجعه بعدی ثبت گردد.
ج) اطلاعات نهایی بازگشت داده شود.

در اینجا قسمت مراجعه به منبع داده، توسط Func به استفاده کننده از متد CacheRead واگذار شده است. به این صورت ما فقط می‌دونیم که یک تابع در اختیار این متد قرار خواهد گرفت که در زمان مناسب می‌شود آن را فراخوانی کرد.
مثلا در مثالی که در انتهای بحث است یک نمونه از کاربرد آن‌را مشاهده می‌کنید.

مطالب
روش یکی کردن پروژه‌های React و ASP.NET Core
یک روش کار کردن با پروژه‌های SPA، توسعه‌ی مجزای قسمت‌های front-end و back-end است. برای مثال پروژه‌ی React را به صورت جداگانه‌ای توسعه می‌دهیم، پروژه‌ی ASP.NET Core را نیز به همین صورت. هنگام آزمایش برنامه، در یکی دستور npm start را اجرا می‌کنیم تا وب سرور آزمایشی React، آن‌را در آدرس http://localhost:3000 قابل دسترسی کند و در دیگری دستور dotnet watch run را صادر می‌کنیم تا برنامه‌ی وب ASP.NET Core را بر روی آدرس https://localhost:5001 مهیا کند. سپس برای اینکه از پورت 3000 بتوان با پورت 5001 کار کرد، نیاز خواهد بود تا CORS را در برنامه‌ی ASP.NET Core فعالسازی کنیم. در حین ارائه‌ی نهایی برنامه نیز هر کدام را به صورت مجزا publish کرده و بعد هم خروجی نهایی پروژه‌ی SPA را در پوشه‌ی wwwroot برنامه‌ی وب کپی می‌کنیم تا قابل دسترسی و استفاده شود. روش دیگری نیز برای یکی/ساده سازی این تجربه وجود دارد که در این مطلب به آن خواهیم پرداخت.


پیشنیاز: ایجاد یک برنامه‌ی خالی React و ASP.NET Core

یک پوشه‌ی خالی را ایجاد کرده و در آن دستور dotnet new react را صادر کنید، تا قالب خاص پروژه‌های React یکی سازی شده‌ی با پروژه‌های ASP.NET Core، یک پروژه‌ی جدید را ایجاد کند.


همانطور که در تصویر فوق نیز مشاهده می‌کنید، این پروژه از دو برنامه تشکیل شده‌است:
الف) برنامه‌ی SPA که در پوشه‌ی ClientApp قرار گرفته‌است و شامل کدهای کامل یک برنامه‌ی React است.
ب) برنامه‌ی سمت سرور ASP.NET Core که یک برنامه‌ی متداول وب، به همراه فایل Startup.cs و سایر فایل‌های مورد نیاز آن است.

در ادامه نکات ویژه‌ی ساختار این پروژه را بررسی خواهیم کرد.


تجربه‌ی توسعه‌ی برنامه‌ها توسط این قالب ویژه

اکنون اگر این پروژه‌ی وب را برای مثال با فشردن دکمه‌ی F5 و یا اجرای دستور dotnet run، اجرا کنیم، چه اتفاقی رخ می‌دهد؟
- به صورت خلاصه برنامه‌ی ASP.NET Core شروع به کار کرده و سبب ارائه همزمان برنامه‌ی SPA نیز خواهد شد.
- پورتی که برنامه‌ی وب بر روی آن قرار دارد، با پورتی که برنامه‌ی React بر روی روی آن ارائه می‌شود، یکی است. یعنی نیازی به تنظیمات CORS را ندارد.
- در این حالت اگر در برنامه‌ی React تغییری را ایجاد کنیم (در هر قسمتی از آن)، hot reloading آن هنوز هم برقرار است و سبب بارگذاری مجدد برنامه‌ی SPA در مرورگر خواهد شد و برای اینکار نیازی به توقف و راه اندازی مجدد برنامه‌ی ASP.NET Core نیست.

اما این تجربه‌ی روان کاربری و توسعه، چگونه حاصل شده‌است؟


بررسی ساختار فایل Startup.cs یک پروژه‌ی مبتنی بر dotnet new react

برای درک نحوه‌ی عملکرد این قالب ویژه، نیاز است از فایل Startup.cs آن شروع کرد.
// ...
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;

namespace dotnet_template_sample
{
    public class Startup
    {
        // ...

        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllersWithViews();

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });
        }
در ابتدا تعریف فضای نام SpaServices را مشاهده می‌کنید. بسته‌ی متناظر با آن در فایل csproj برنامه به صورت زیر ثبت شده‌است:
<ItemGroup>
   <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.2" />
</ItemGroup>
این بسته، همان بسته‌ی جدید SpaServices است و در NET 5x. نیز پشتیبانی خواهد شد .

در متد ConfigureServices، ثبت سرویس‌های مرتبط با فایل‌های استاتیک پروژه‌ی SPA، توسط متد AddSpaStaticFiles صورت گرفته‌است. در اینجا RootPath آن، به پوشه‌ی ClientApp/build اشاره می‌کند. البته این پوشه هنوز در این ساختار، قابل مشاهده نیست؛ اما زمانیکه پروژه‌ی ASP.NET Core را برای ارائه‌ی نهایی، publish کردیم، به صورت خودکار ایجاد شده و حاوی فایل‌های قابل ارائه‌ی برنامه‌ی React نیز خواهد بود.

قسمت مهم دیگر کلاس آغازین برنامه، متد Configure آن است:
// ...
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;

namespace dotnet_template_sample
{
    public class Startup
    {
        // ...

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ...
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });
        }
    }
}
در اینجا ثبت سه میان افزار جدید را مشاهده می‌کنید:
- متد UseSpaStaticFiles، سبب ثبت میان‌افزاری می‌شود که امکان دسترسی به فایل‌های استاتیک پوشه‌ی ClientApp حاوی برنامه‌ی React را میسر می‌کند؛ مسیر این پوشه را در متد ConfigureServices تنظیم کردیم.
- متد UseSpa، سبب ثبت میان‌افزاری می‌شود که دو کار مهم را انجام می‌دهد:
1- کار اصلی آن، ثبت مسیریابی معروف catch all است تا مسیریابی‌هایی را که توسط کنترلرهای برنامه‌ی ASP.NET Core مدیریت نمی‌شوند، به سمت برنامه‌ی React هدایت کند. برای مثال مسیر https://localhost:5001/api/users به یک کنترلر API برنامه‌ی سمت سرور ختم می‌شود، اما سایر مسیرها مانند https://localhost:5001/login قرار است صفحه‌ی login برنامه‌ی سمت کلاینت SPA را نمایش دهند و متناظر با اکشن متد خاصی در کنترلرهای برنامه‌ی وب ما نیستند. در این حالت، کار این مسیریابی catch all، نمایش صفحه‌ی پیش‌فرض برنامه‌ی SPA است.
2- بررسی می‌کند که آیا شرایط IsDevelopment برقرار است؟ آیا در حال توسعه‌ی برنامه هستیم؟ اگر بله، میان‌افزار دیگری را به نام UseReactDevelopmentServer، اجرا و ثبت می‌کند.

برای درک عملکرد میان‌افزار ReactDevelopmentServer نیاز است به سورس آن مراجعه کرد. این میان‌افزار بر اساس پارامتر start ای که دریافت می‌کند، سبب اجرای npm run start خواهد شد. به این ترتیب دیگر نیازی به اجرای جداگانه‌ی این دستور نخواهد بود و همچنین این اجرا، به همراه تنظیمات proxy مخصوصی نیز هست تا پورت اجرایی برنامه‌ی React و برنامه‌ی ASP.NET Core یکی شده و دیگر نیازی به تنظیمات CORS مخصوص برنامه‌های React نباشد. بنابراین hot reloading ای که از آن صحبت شد، توسط ASP.NET Core مدیریت نمی‌شود. در پشت صحنه همان npm run start اصلی برنامه‌های React، در حال اجرای وب سرور آزمایشی React است که از hot reloading پشتیبانی می‌کند.

یک مشکل: با این تنظیم، هربار که برنامه‌ی ASP.NET Core اجرا می‌شود (به علت تغییرات در کدها و فایل‌های پروژه)، سبب اجرای مجدد و پشت صحنه‌ی react development server نیز خواهد شد که ... آغاز برنامه را در حالت توسعه، کند می‌کند. برای رفع این مشکل می‌توان این وب سرور توسعه‌ی برنامه‌های React را به صورت جداگانه‌ای اجرا کرد و فقط تنظیمات پروکسی آن‌را در اینجا ذکر نمود:
// replace
spa.UseReactDevelopmentServer(npmScript: "start");
// with
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
در اینجا فقط کافی است سطر UseReactDevelopmentServer را با تنظیم UseProxyToSpaDevelopmentServer که به آدرس وب سرور توسعه‌ی برنامه‌های React اشاره می‌کند، تنظیم کنیم. بدیهی است در اینجا حالت باید از طریق خط فرمان به پوشه‌ی clientApp وارد شد و دستور npm start را یکبار به صورت دستی اجرا کرد، تا این وب سرور، راه اندازی شود.


تغییرات ویژه‌ی فایل csproj برنامه

اگر به فایل csproj برنامه دقت کنیم، دو تغییر جدید نیز در آن قابل مشاهده هستند:
الف) نصب خودکار وابستگی‌های برنامه‌ی client
  <Target Name="DebugEnsureNodeEnv"
     BeforeTargets="Build" 
     Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
    <!-- Ensure Node.js is installed -->
    <Exec Command="node --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  </Target>
در این تنظیم، در حالت build و debug، ابتدا بررسی می‌کند که آیا پوشه‌ی node_modules برنامه‌ی SPA وجود دارد؟ اگر خیر، ابتدا مطمئن می‌شود که node.js بر روی سیستم نصب است و سپس دستور npm install را صادر می‌کند تا تمام وابستگی‌های برنامه‌ی client، دریافت و نصب شوند.

ب) یکی کردن تجربه‌ی publish برنامه‌ی ASP.NET Core با publish پروژه‌های React
  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />

    <!-- Include the newly-built files in the publish output -->
    <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>
میان‌افزار ReactDevelopmentServer کار اجرا و پروکسی دستور npm run start را در حالت توسعه انجام می‌دهد. اما در حالت ارائه‌ی نهایی چطور؟ در اینجا نیاز است دستور npm run build اجرا شده و فایل‌های مخصوص ارائه‌ی نهایی برنامه‌ی React تولید و سپس به پوشه‌ی wwwroot، کپی شوند. تنظیم فوق، دقیقا همین کار را در حین publish برنامه‌ی ASP.NET Core، به صورت خودکار انجام می‌دهد و شامل این مراحل است:
-  ابتدا npm install را جهت اطمینان از به روز بودن وابستگی‌های برنامه مجددا اجرا می‌کند.
- سپس npm run build را برای تولید فایل‌های قابل ارائه‌ی برنامه‌ی React اجرا می‌کند.
- در آخر تمام فایل‌های پوشه‌ی ClientApp/build تولیدی را به بسته‌ی نهایی توزیعی برنامه‌ی ASP.NET Core، اضافه می‌کند.
مطالب
React 16x - قسمت 30 - React Hooks - بخش 1 - معرفی useState و useEffect
با استفاده از React Hooks که در نگارش 16.7.0 آن معرفی شدند، می‌توان در کامپوننت‌های تابعی «تا پیش از این» بدون حالت، به state و تمام قابلیت‌های دیگر React، دسترسی یافت. جهت یادآوری، در قسمت 8 این سری، کامپوننت‌های تابعی بدون حالت را معرفی کردیم. تا پیش از معرفی React Hooks، برای ردیابی تغییرات مقادیری خاص، می‌بایستی آن‌ها را در خاصیت state کامپوننت‌هایی که به صورت کلاس تعریف شده بودند، قرار می‌دادیم. بنابراین class components، تنها نوع کامپوننت‌هایی در React بودند که دسترسی به state را داشتند. شبیه به همین مورد، برای life-cycle hooks معرفی شده‌ی در قسمت 9 برقرار بود. برای مثال متد componentDidMount، تنها در class components، جهت دسترسی به یک API خارجی و انجام اعمال Ajax ای، قابل تعریف بود و کار کامپوننت‌های تابعی بدون حالت، بیشتر نمایش عناصر HTML و دریافت مقادیر خود از class components، توسط props بود. به این ترتیب امکان تجزیه‌ی یک UI پیچیده، به یک component tree با خوانایی بیشتری، میسر می‌شد. اما با معرفی React v16.7.0، از لحاظ فنی دیگر الزاما نیازی به class components وجود ندارد و می‌توان با استفاده از React Hooks، تمام قابلیت‌هایی را که پیشتر فقط توسط class components در اختیار داشتیم، اکنون با کامپوننت‌های تابعی نیز پیاده سازی کنیم.


برپایی پیش‌نیازها

در اینجا برای بررسی React Hooks، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> npm i -g create-react-app
> create-react-app sample-30
> cd sample-30
> npm start
در ادامه توئیتر بوت استرپ 4 را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save bootstrap
سپس برای افزودن فایل bootstrap.css به پروژه‌ی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
این import به صورت خودکار توسط webpack ای که در پشت صحنه کار bundling & minification برنامه را انجام می‌دهد، مورد استفاده قرار می‌گیرد.

همچنین اگر به فایل package.json موجود در ریشه‌ی پروژه دقت کنیم، برای کار با React-hooks، نیاز است نگارش بسته‌های React و React-dom، حداقل مساوی 16.7 باشند که در زمان نگارش این مطلب، نگارش 16.12.0 آن به صورت خودکار نصب می‌شود. بنابراین بدون مشکلی می‌توانیم شروع به کار با React hooks کنیم.


معرفی useState Hook

در اینجا قصد داریم یک شمارشگر را به همراه یک دکمه، در صفحه نمایش دهیم؛ بطوریکه این شمارشگر، تعداد بار کلیک بر روی دکمه را ردیابی می‌کند. از پیش می‌دانیم که برای ردیابی مقدار تعداد بار کلیک شدن، باید متغیر آن‌را درون state یک class component قرار داد:
import "./App.css";

import React, { Component } from "react";

class App extends Component {
  state = { count: 0 };

  incrementCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <button onClick={this.incrementCount} className="btn btn-primary m-3">
        I was clicked {this.state.count} times!
      </button>
    );
  }
}

export default App;
در اینجا یک class component متداول را داریم که در آن دکمه‌ای تعریف شده‌است. سپس برای شمارش تعداد بار کلیک بر روی آن، خاصیت count را به شیء منتسب به state، با مقدار اولیه‌ی صفر، اضافه کرده‌ایم. اکنون هربار که بر روی آن کلیک می‌شود، رویدادگردان incrementCount فراخوانی شده و توسط متد this.setState، مقدار پیشین خاصیت count، بر اساس مقدار فعلی آن، یک واحد افزایش می‌یابد. نتیجه‌ی آن نیز در برچسب دکمه، نمایش داده می‌شود:


اکنون می‌خواهیم همین کامپوننت را توسط React hooks بازنویسی کنیم. در ابتدا، فایل app.js را به App‍Class.js، تغییر نام می‌دهیم تا نگارش قبلی class component را برای مقایسه، در اختیار داشته باشیم. در ادامه فایل جدید AppFunction.js را برای بازنویسی آن توسط یک کامپوننت تابعی، توسط میانبرهای imrc و سپس sfc در VSCode، ایجاد می‌کنیم. البته این تغییر نام فایل‌ها، نیاز به تغییر نام ماژول‌های import شده‌ی در فایل index.js را نیز به صورت زیر دارد:
//import App from "./App‍Class";
import App from "./AppFunction";

اولین سؤالی که اینجا مطرح می‌شود، این است: در این کامپوننت تابعی جدید، state را از کجا بدست بیاوریم؟
با React Hooks، بجای تعریف یک state به صورت خاصیت، آن‌را صرفا use می‌کنیم و این دقیقا نام اولین React Hooks ای است که بررسی می‌کنیم؛ یا همان useState. بنابراین ابتدا این شیء را import خواهیم کرد:
import React, { useState } from 'react';
useState یک تابع است و باید در ابتدای کامپوننت، مورد استفاده قرار گیرد. این متد برای شروع به کار، نیاز به یک state آغازین را دارد؛ دقیقا مانند همان‌کاری که در class component فوق انجام دادیم:
const App = () => {
    const [count, setCount] = useState(0);
در اینجا عدد صفر، همان مقدار آغازین متغیر count است (شبیه به کاری که در state = { count: 0 } انجام دادیم). سپس اولین خروجی متد useState که داخل یک آرایه مشخص شده‌است (JavaScript array destructuring ؛ با مزیت امکان انتخاب نام‌هایی دلخواه، بدون نیاز به تعریف alias، برخلاف حالت object destructuring)، همان متغیر count است که توسط state ردیابی خواهد شد. اینبار بجای متد this.setState قبلی که یک متد عمومی بود، متدی اختصاصی را صرفا جهت تغییر مقدار همین متغیر count، به نام setCount به عنوان دومین خروجی متد useState، تعریف می‌کنیم. در حقیقت تا اینجا امضای متد جنریک useState استفاده شده، به صورت زیر تغییر کرده‌است:
useState<number>(initialState: number | (() => number)): [number, React.Dispatch<React.SetStateAction<number>>]
متد useState، یک initialState را دریافت می‌کند و سپس یک عدد را (در اینجا چون مقدار آغازین، عددی است)، به همراه یک متد، جهت تغییر state آن، بازگشت می‌دهد:
import React, { useState } from "react";

const App = () => {
  const [count, setCount] = useState(0);
  const incrementCount = () => {
    setCount(count + 1);
  };

  return (
    <button onClick={incrementCount} className="btn btn-primary m-3">
      I was clicked {count} times!
    </button>
  );
};

export default App;
- پس از تعریف useState، متد رویدادگردان onClick را همانند قبل تعریف می‌کنیم؛ با یک تغییر مهم: چون این متد داخل یک کامپوننت تابعی تعریف شده‌است، باید با const شروع شود و همانند یک متغیر که به آن متدی انتساب داده شده، معرفی گردد. پیشتر incrementCount تعریف شده‌ی داخل یک class component، یک خاصیت بود که متدی به آن انتساب داده شده بود.
- همچنین در اینجا (داخل این متد) دیگر خبری از this‌ها نیست؛ onClick، مستقیما به متغیر incrementCount اشاره می‌کند و {count} نیز مستقیما از خروجی useState، که به مقدار جاری count اشاره می‌کند، تامین می‌شود.
- اکنون با هربار کلیک بر روی این دکمه، متد منتسب به متغیر incrementCount فراخوانی شده و در داخل آن، همان متد setCount را جهت به روز رسانی state، فراخوانی می‌کنیم (بجای فراخوانی this.setState عمومی قبلی). در اینجا ابتدا مقدار جاری متغیر count در state، دریافت شده و سپس یک واحد به آن اضافه می‌شود. امضای متد جنریک setCount به صورت زیر است:
 const setCount: (value: React.SetStateAction<number>) => void


استفاده از مقدار قبلی state توسط useState

زمانیکه متد this.setState فراخوانی می‌شود، اینکار سبب در صف قرار گرفتن رندر مجدد کامپوننت جاری خواهد شد. همچنین اعمال این متد نیز ممکن است در صف قرار گیرد. یعنی اگر پس از فراخوانی this.setState، سعی در خواندن state به روز شده را داشته باشیم، ممکن است مقدار اشتباهی را دریافت کنیم:
  incrementCount = () => {
    this.setState({ count: this.state.count + 1 });
  };
برای نمونه در فراخوانی فوق، منظور از this.state.count، مقدار جاری یا همان مقدار قبلی count است که قرار است یک واحد به آن اضافه شود. اما چون متد this.setState کار به روز رسانی‌های state را نیز در صف قرار می‌دهد، دفعه‌ی بعدی که بر روی آن کلیک شد، الزامی ندارد که this.state.count، حتما در همان لحظه به روز رسانی شده باشد. برای رفع این مشکل می‌توان نوشت:
  incrementCount = () => {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  };
prevState در اینجا یعنی state قبلی و توسط متد setState، به صورت یک arrow function قابل دریافت است که در نهایت یک شیء را باز می‌گرداند. اکنون بر اساس state قبلی می‌توان به روز رسانی دقیقی را انجام داد.

این نکته در مورد کامپوننت‌های تابعی نیز وجود دارد:
  const incrementCount = () => {
    setCount(count + 1);
  };
در اینجا متغیر count، همانند خاصیت this.state.count کامپوننت‌های کلاسی عمل می‌کند. بنابراین الزامی ندارد که با هر بار فراخوانی setCount، مقدار جاری متغیر count، دقیقا به مقدار قبلی تنظیم شده‌ی آن اشاره کند. برای انجام مشابه اینکار با کامپوننت‌های تابعی که از useState استفاده می‌کنند، می‌توان به صورت زیر عمل کرد:
const incrementCount = () => {
    setCount(prevCount => prevCount + 1);
  };
در اینجا نیز امکان دسترسی به مقدار قبلی count، توسط یک arrow function وجود دارد که برخلاف حالت prevState، فقط یک مقدار عددی مرتبط با count را باز می‌گرداند.


به روز رسانی بیش از یک خاصیت در state

فرض کنید قصد داریم به مثال جاری، یک مربع را در صفحه اضافه کنیم که با کلیک بر روی آن، رنگش تغییر می‌کند (خاموش و روشن می‌شود):


در حالت AppClass یا کامپوننت کلاسی، کدهای برنامه به صورت زیر تغییر می‌کنند:
import "./App.css";

import React, { Component } from "react";

class App extends Component {
  state = {
    count: 0,
    isOn: false
  };

  incrementCount = () => {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  };

  toggleLight = () => {
    this.setState(prevState => ({
      isOn: !prevState.isOn
    }));
  };

  render() {
    return (
      <>
        <h1>App Class</h1>
        <h2>Counter</h2>
        <button onClick={this.incrementCount} className="btn btn-primary m-3">
          I was clicked {this.state.count} times!
        </button>

        <h2>Toggle Light</h2>
        <div
          style={{
            height: "50px",
            width: "50px",
            cursor: "pointer"
          }}
          className={
            this.state.isOn ? "alert alert-info m-3" : "alert alert-warning m-3"
          }
          onClick={this.toggleLight}
        />
      </>
    );
  }
}

export default App;
توضیحات:
- در متد رندر، نیاز است تا تنها یک child، بازگشت داده شود. به همین جهت می‌توان از React.Fragment، برای محصور سازی المان‌های تعریف شده، استفاده کرد. البته در React 16.7.0، دیگر نیازی به ذکر صریح React.Fragment نبوده و فقط می‌توان نوشت </><> تا بیانگر یک فرگمنت باشد.
- سپس یک div تعریف شده که با استفاده از ویژگی style، یک سری شیوه‌نامه‌ی ابتدایی، مانند طول و عرض و نوع اشاره‌گر ماوس آن، تعیین شده‌اند.
- اکنون برای اینکه بتوان با کلیک بر روی این div، رنگ آن‌را تغییر داد، نیاز است بتوان توسط متغیری، مقدار خاموش و روشن بودن را ردیابی کرد. به همین جهت خاصیت جدید isOn را به state اضافه می‌کنیم.
- در آخر، رویداد onClick این div را به متد رویدادگران toggleLight متصل می‌کنیم تا توسط آن و فراخوانی this.setState، بتوان مقدار قبلی isOn را در state، دریافت و سپس آن‌را معکوس کرد و بجای مقدار جاری isOn در state درج کنیم. این فراخوانی، سبب رندر مجدد کامپوننت جاری شده و در نتیجه‌ی آن، مقدار className را بر اساس مقدار this.state.isOn، به صورت پویا تغییر می‌دهد.

برای مشاهده‌ی خروجی برنامه در این حالت، نیاز است به index.js مراجعه و تغییر زیر را اعمال کرد تا کامپوننت App، از ماژول App‍Class تامین شود:
import App from "./App‍Class";
//import App from "./AppFunction";

اکنون قصد داریم دقیقا معادل همین قطعه کد را در کامپوننت تابعی خود پیاده سازی کنیم. به همین جهت به فایل src\AppFunction.js بازگشته و تغییرات زیر را اعمال می‌کنیم:
import React, { useState } from "react";

const App = () => {
  const [count, setCount] = useState(0);
  const [isOn, setIsOn] = useState(false);

  const incrementCount = () => {
    setCount(prevCount => prevCount + 1);
  };

  const toggleLight = () => {
    setIsOn(prevIsOn => !prevIsOn);
  };

  return (
    <>
      <h1>App Function</h1>
      <h2>Counter</h2>
      <button onClick={incrementCount} className="btn btn-primary m-3">
        I was clicked {count} times!
      </button>

      <h2>Toggle Light</h2>
      <div
        style={{
          height: "50px",
          width: "50px",
          cursor: "pointer"
        }}
        className={isOn ? "alert alert-info m-3" : "alert alert-warning m-3"}
        onClick={toggleLight}
      />
    </>
  );
};

export default App;
توضیحات:
- اگر دقت کنید، کلیات این کامپوننت تابعی، با کامپوننت کلاسی، آنچنان متفاوت نیست. متد رندر آن دقیقا همان markup را بازگشت می‌دهد؛ با یک تفاوت مهم: در اینجا دیگر نیازی به ذکر thisها نیست، چون تمام ارجاعات، به متغیرهای داخل تابع App انجام شده‌است و نه به متدها و یا خاصیت‌های یک کلاس.
- مرحله‌ی بعد تغییر، جایگزینی this.state.isOn قبلی، با یک متغیر درون تابع App است. به همین جهت در اینجا یک useState دیگر را تعریف می‌کنیم. هر useState، تنها به قسمتی از state اشاره می‌کند و مانند خاصیت کلی state مربوط به یک کلاس نیست. همچنین در خاصیت state یک کلاس، مقدار آن همواره به یک شیء اشاره می‌کند؛ اما با useState چنین اجباری وجود ندارد و می‌تواند هر نوع مجاز جاوا اسکریپتی باشد. برای مثال در اینجا مقدار اولیه‌ی آن به false تنظیم شده‌است. پس از آن، خروجی این متد، قسمتی از state را که کنترل می‌کند، به نام متغیر isOn و تنظیم کننده‌ی آن‌را به نام متد setIsOn، معرفی خواهد کرد. متد useState، یک متد جنریک است. یعنی نوع خروجی‌های آن بر اساس نوع مقدار اولیه‌ای که به آن انتساب داده می‌شود، تعیین می‌شود. برای مثال اگر نوع مقدار اولیه‌ی آن، Boolean باشد، به صورت خودکار نوع متغیر و پارامتر متد خروجی از آن نیز Boolean خواهند بود.
- در آخر خاصیت toggleLight کلاس کامپوننت، تبدیل به یک متغیر یا ثابت، در تابع جاری App می‌شود و بجای this.setState کلی قبلی، از متد اختصاصی‌تر setIsOn، برای تغییر مقدار state متناظر، کمک گرفته خواهد شد. در اینجا با استفاده از prevIsOn، به مقدار دقیق پیشین متغیر isOn در state، دسترسی یافته و سپس آن‌را معکوس می‌کنیم.

برای مشاهده‌ی خروجی برنامه در این حالت، نیاز است به index.js مراجعه و تغییر زیر را اعمال کرد تا کامپوننت App، از ماژول App‍Function تامین شود:
// import App from "./App‍Class";
import App from "./AppFunction";


معرفی useEffect Hook

فرض کنید قصد داریم برچسب دکمه‌ی «I was clicked {this.state.count} times» را در المان Title صفحه، نمایش دهیم. برای انجام چنین کاری نیاز است با DOM API تعامل داشته باشیم؛ اما پیشتر یک چنین کاری را تنها با class components می‌شد انجام داد. برای رفع این محدودیت در کامپوننت‌های تابعی، hook جدیدی به نام useEffect، ارائه شده‌است که باید import شود:
import React, { useEffect, useState } from "react";
اکنون این سؤال مطرح می‌شود که در متد useEffect، واژه‌ی Effect به چه چیزی اشاره می‌کند؟
در اینجا effect به معنای side effect و یا اثرات جانبی است؛ مانند: تعامل با یک API خارجی، کار با API مرورگر و کلا هر جائیکه در برنامه با دنیای خارج تعاملی وجود دارد، به عنوان یک side effect شناخته می‌شود. بنابراین با استفاده متد useEffect، می‌توان در کامپوننت‌های تابعی نیز با دنیای خارج، تعامل برقرار کرد.
import React, { useEffect, useState } from "react";

const App = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You have clicked ${count} times`;
  });
متد useEffect، یک تابع را به عنوان ورودی دریافت کرده (effect function) و آن‌را پس از هر بار رندر کامپوننت جاری، اجرا می‌کند. برای مثال با تغییر state، کار رندر کامپوننت جاری، در صف قرار می‌گیرد و پس از اتمام رندر، تابع effect ذکر شده، اجرا می‌شود. برای مثال در اینجا پس از هر بار رندر کامپوننت،  document.title با عبارتی که به همراه تعداد بار کلیک کردن بر روی دکمه‌است، به روز رسانی می‌شود:


در اولین بار اجرای برنامه، عبارت «You have clicked 0 times»، در عنوان صفحه‌ی جاری، ظاهر می‌شود که از مقدار پیش‌فرض count، استفاده کرده‌است. اکنون اگر بر روی دکمه، کلیک کنیم، پس از تغییر برچسب دکمه (یا همان رندر کامپوننت، پس از تغییری در state)، آنگاه عنوان نمایش داده شده‌ی در مرورگر نیز تغییر می‌کند.

یک نکته: چون useEffect دارای همان scope متغیر count است، نیاز به API خاصی برای خواندن مقدار آن در اینجا نیست.

سؤال: برای پیاده سازی چنین قابلیتی در یک کامپوننت کلاسی چه باید کرد؟ در این مثال، در ابتدای کار باید مقدار پیش‌فرض موجود در state را در عنوان صفحه مشاهده کرد و پس از هربار به روز رسانی state نیز باید این عنوان، تغییر کند.
برای پیاده سازی معادل متد useEffect ای که در یک کامپوننت تابعی استفاده شد، در اینجا باید از دو life-cycle hook متفاوت، به نام‌های componentDidMount و componentDidUpdate، استفاده کنیم:
class App extends Component {

  componentDidMount() {
    document.title = `You have been clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You have been clicked ${this.state.count} times`;
  }
- برای به روز رسانی عنوان صفحه، در اولین بار نمایش آن، باید از متد componentDidMount استفاده کرد.
- همچنین چون می‌خواهیم به ازای هر تغییری در state نیز این عنوان تغییر کند (با هر بار کلیک بر روی دکمه)، باید از متد componentDidUpdate هم استفاده کنیم.


پاکسازی اثرات جانبی در useEffect Hook

فرض کنید قصد داریم موقعیت فعلی کرسر ماوس را در مرورگر نمایش دهیم. برای انجام اینکار در کامپوننت کلاسی، می‌توان از متد componentDidMount جهت دسترسی به DOM API و استفاده از متد addEventListener آن، برای گوش فرا دادن به حرکات کرسر ماوس، استفاده کرد:
class App extends Component {

  componentDidMount() {
    // ...
    window.addEventListener("mousemove", this.handleMouseMove);
  }
 دومین پارامتر این متد، یک callback function است که این حرکات را ردیابی می‌کند:
  handleMouseMove = event => {
    this.setState({
      x: event.pageX,
      y: event.pageY
    });
  };
در اینجا می‌توان با استفاده از شیء رخداد دریافتی، موقعیت x و y کرسر ماوس را در صفحه دریافت کرد و سپس با فراخوانی متد this.setState، این خواص را در state کامپوننت، اضافه و یا به روز رسانی نمود. بنابراین در این حالت الزامی به تعریف این خواص در شیء منتسب به state نیست؛ اما حداقل با تعریف آن‌ها می‌توان مقدار اولیه‌ای را برایشان درنظر گرفت:
class App extends Component {
  state = {
    //...
    x: 0,
    y: 0
  };
در آخر برای نمایش این اطلاعات موجود در state، می‌توان چنین المان‌هایی را به متد رندر کامپوننت، اضافه کرد:
<h2>Mouse Position</h2>
<p>X position: {this.state.x}</p>
<p>Y position: {this.state.y}</p>
بنابراین ردیابی تغییرات محل کرسر ماوس نیز یک side effect است که برای دسترسی به آن، نیاز است با window API کار کرد و این side effect‌ها دو نوع هستند:
- تعدادی از آن‌ها نیازی به پاکسازی و خارج شدن از حافظه را ندارند؛ مانند به روز رسانی عنوان صفحه در مرورگر. می‌توان یک چنین side effect هایی را اجرا و سپس آن‌ها را فراموش کرد.
- اما تعدادی از side effect‌ها را حتما باید پاکسازی کرد؛ مانند mousemove listener تعریف شده‌ی در مثال فوق. در اینجا زمانیکه کامپوننت جاری mount می‌شود، این listener را تعریف می‌کنیم؛ اما با Unmount شدن آن، باید این listener را حذف کرد تا برنامه دچار نشتی حافظه نشود (اگر اینکار انجام نشود، در این مثال مرورگر هنگ خواهد کرد!). روش انجام اینکار در متد componentWillUnmount، به صورت زیر است:
  componentWillUnmount() {
    window.removeEventListener("mousemove", this.handleMouseMove);
  }
سؤال: یک چنین قابلیتی را چگونه می‌توان در یک کامپوننت تابعی تعریف کرد؟
در این حالت نیز می‌توان از متد useEffect استفاده کرد. البته ابتدا باید state شیء ای را برای نگهداری اطلاعات به روز موقعیت مکانی کرسر ماوس، ایجاد کرد:
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
همانطور که عنوان شد، متد useState، برخلاف خاصیت کلی state یک کامپوننت کلاسی که فقط اشیاء را می‌پذیرد، هر نوع جاوا اسکریپتی را می‌تواند بپذیرد که در اینجا برای نمونه به یک شیء، تنظیم شده‌است.
سپس side effect خود را در قسمت effect function متد useEffect قرار می‌دهیم که آن نیز به متغیر handleMouseMove اشاره می‌کند:
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    // ...
    window.addEventListener("mousemove", handleMouseMove);
  });

  const handleMouseMove = event => {
    setMousePosition({
      x: event.pageX,
      y: event.pageY
    });
  };
به متغیر handleMouseMove، متدی انتساب داده شده‌است که با فراخوانی آن توسط mousemove listener، یک شیء evenet دریافت و سپس بر اساس خواص آن، خواص x و y شیء موجود در state، توسط متد setMousePosition، به روز رسانی می‌شود.
سپس برای نمایش x و y به روز رسانی شده‌ی در state، می‌توان از markup زیر در متد رندر استفاده کرد.
<h2>Mouse Position</h2>
{JSON.stringify(mousePosition, null, 2)}
<br />
اکنون سؤال اینجا است که معادل متد componentWillUnmount در اینجا چیست؟
اگر effect function تعریف شده، دارای یک خروجی (از نوع تابع) باشد، به این معنا است که این side effect، نیاز به پاکسازی دارد و این متد را در زمان Unmount آن فراخوانی می‌کند:
  useEffect(() => {
    // …
    // componentDidMount & componentDidUpdate
    window.addEventListener("mousemove", handleMouseMove);

    // componentWillUnmount
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
    };
  });
پیشتر آموختیم که effect function، تنها یکبار اجرا نمی‌شود؛ بلکه به ازای هر بار رندر، یکبار پس از آن اجرا می‌شود. یعنی خروجی آن نه فقط در زمان Unmount اجرا می‌شود، بلکه یکبار هم پیش از هر بار اجرای خود effect اجرا می‌گردد. به این ترتیب در اینجا فرصت پاکسازی اجرای قبلی را نیز خواهیم داد. بنابراین روش پاکسازی آن نسبت به متد componentWillUnmount کامپوننت‌های تابعی، پیشرفته‌تر است. یعنی توسط آن می‌توان یک side effect خاص را پیش و پس از هر بار رندر، در صورت نیاز فراخوانی کرد.

سؤال: اگر بخواهیم از اجرای یک side effect، به ازای هر بار رندر جلوگیری کنیم، چه باید کرد؟

برای اینکار می‌توان آرگومان دومی را به متد useEffect اضافه کرد که آرایه‌ای از مقادیر است. توسط اعضای آن می‌توان مقدار و یا مقادیری را مشخص کرد که side effect تعریف شده، به آن وابسته‌است. اکنون اگر این مقدار تغییر کند، آنگاه side effect متناظر با آن نیز اجرا می‌شود:
  useEffect(() => {
    document.title = `You have clicked ${count} times`;
    window.addEventListener("mousemove", handleMouseMove);

    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
    };
  },[]);
در این مثال، چون پارامتر دوم را به صورت یک آرایه‌ی خالی مقدار دهی کرده‌ایم، effect function تعریف شده تنها در زمان mount و unmount اجرا می‌شود. البته اگر این تغییر را ذخیره کرده و برنامه را اجرا کنیم، تمام قسمت‌های تعریف شده‌ی آن به خوبی کار می‌کنند (با کلیک بر روی دکمه، برچسب آن به روز رسانی می‌شود و یا با حرکت کرسر ماوس در صفحه، موقعیت آن گزارش داده می‌شود)، منهای قسمت به روز رسانی عنوان صفحه با هر بار کلیک. علت اینجا است که با تغییر معرفی شده، دیگر حالت componentDidUpdate توسط متد useEffect پوشش داده نمی‌شود و به این ترتیب با هر بار به روز رسانی state و رندر کامپوننت، دیگر کار به اجرای مجدد side effect آن نمی‌رسد.
برای رفع این مشکل، باید به useEffect اعلام کنیم که side effect تعریف شده‌ی در آن، وابسته‌است به مقدار count و با تغییرات آن در state، نیاز است مجددا اجرا شود:
  useEffect(() => {
   // ...
  },[count]);
به همین جهت در آرایه‌ی تعریف شده، مقداری را که به آن وابستگی وجود دارد، مشخص می‌کنیم. با این تغییر اگر بر روی دکمه کلیک کنیم، چون اکنون useEffect می‌داند که باید تغییرات count را ردیابی کند، با اجرای مجدد effect function خود، عنوان صفحه نیز به روز رسانی خواهد شد.


کار با چندین listener مختلف در متد useEffect

سؤال: آیا تنظیم یک وابستگی خاص در متد useEffect، امکان تنظیم event listenerهای دیگر را غیرممکن می‌کند؟
برای پاسخ به این سؤال، چند event listener دیگر را ثبت می‌کنیم. برای مثال یکی دیگر از APIهای مرورگر، navigator نام دارد که توسط آن می‌توان وضعیت آنلاین و آفلاین بودن را به کمک خروجی خاصیت navigator.onLine آن، مشخص کرد. به کمک این API می‌خواهیم این وضعیت را نمایش دهیم. برای این منظور ابتدا state آن‌را در کامپوننت تابعی، ایجاد می‌کنیم:
const [status, setStatus] = useState(navigator.onLine);
مقدار اولیه‌ی این state را نیز توسط مقدار جاری خاصیت navigator.onLine، مشخص کرده‌ایم.
اکنون برای گوش فرا دادن به تغییرات این خاصیت (online و یا offline شدن کاربر)، نیاز است دو event listener را به کمک متد addEventListener ثبت کنیم و همچنین این متدها نیاز به پاکسازی هم دارند:
  useEffect(() => {
    // ...

    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);

    return () => {
      // ...
  
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, [count]);
پس از ثبت event listener ها، اکنون می‌توان با هربار فراخوانی آن‌ها توسط مرورگر، وضعیت state آن‌را به true و یا false، تغییر داد:
  const handleOnline = () => {
    setStatus(true);
  };

  const handleOffline = () => {
    setStatus(false);
  };
و در آخر مقدار متغیر status را به نحو زیر در متد رندر، نمایش داد:
<h2>Network Status</h2>
<p>
  You are <strong>{status ? "online" : "offline"}</strong>
</p>

برای آزمایش حالت offline آن، فقط کافی است به ابزار توسعه دهندگان مرورگر مراجعه کرده و در برگه‌ی network آن، حالت online را offline کنید:


در این حالت هم نمایش وضعیت آنلاین بودن کاربر به درستی کار می‌کند و هم سایر قسمت‌هایی که تاکنون اضافه کرده‌ایم. به این معنا که هر چند توسط ذکر پارامتر [count]، وابستگی خاصی برای side effect ویژه‌ای، مشخص شده‌است، اما ما را از تنظیم event listener‌های دیگری در قسمت‌های mount و unmount محروم نمی‌کند.


پاکسازی اثرات جانبی در useEffect Hook، زمانیکه روشی برای آن وجود ندارد

در مثالی دیگر می‌خواهیم از API موقعیت جغرافیایی کاربر در مرورگر یا navigator.geolocation استفاده کنیم. توسط این API هم می‌توان طول و عرض جغرافیایی را به دست آورد و هم تغییرات آن‌را تحت نظر قرار داد. برای مثال با بررسی این تغییرات می‌توان سرعت را نیز به دست آورد.
در این حالت نیز ابتدا با تعریف state مختص به آن شروع می‌کنیم و اینبار به عنوان مثال، مقدار اولیه‌ی آن‌را خارج از تابع جاری تنظیم می‌کنیم (جهت نمایش یک گزینه‌ی مهیای دیگر):
const initialLocationState = {
  latitude: null,
  longitude: null,
  speed: null
};

const App = () => {
  // ...
  const [location, setLocation] = useState(initialLocationState);
سپس جهت کار با این API، مقدار اولیه‌ی موقعیت کاربر و همچنین ردیابی تغییرات آن‌را در متد useEffect، تنظیم می‌کنیم:
  useEffect(() => {
    // ...

    navigator.geolocation.getCurrentPosition(handleGeolocation);
    const watchId = navigator.geolocation.watchPosition(handleGeolocation);

    return () => {
      // ...
      navigator.geolocation.clearWatch(watchId);
    };
  }, [count]);
در اینجا متد getCurrentPosition، با متد addEventListener متفاوت است و تنها یک callback function را دریافت می‌کند که در آن می‌توان setLocation را جهت به روز رسانی موقعیت جاری جغرافیایی کاربر، فراخوانی کرد. مشکلی که با این متد وجود دارد، نداشتن معادلی شبیه به متد removeEventListener است. یعنی API آن روشی را برای unmount آن فراهم نکرده‌است. البته متد watchPosition که تغییرات موقعیت کاربر را ردیابی می‌کند، دارای API مخصوص پاکسازی آن نیز هست که در این کدها ذکر شده‌است.

یک روش برای حل این مشکل و غیرفعال کردن دستی listener متد getCurrentPosition پس از unmount، تعریف یک متغیر mounted پیش از متد useEffect است:
  let mounted = true;

  useEffect(() => {
   // ...

    return () => {
      // ...
      mounted = false;
    };
  }, [count]);
و آن‌را در زمان پاکسازی useEffect، با false مقدار دهی می‌کنیم. اکنون زمانیکه می‌خواهیم رویدادگردان این Listener را تعریف کنیم، تنها بر اساس مقدار متغیر mounted عمل خواهیم کرد که از نشتی حافظه‌ی آن جلوگیری می‌کند:
  const handleGeolocation = event => {
    if (mounted) {
      setLocation({
        latitude: event.coords.latitude,
        longitude: event.coords.longitude,
        speed: event.coords.speed
      });
    }
  };
در آخر هم برای نمایش آن در متد رندر به صورت زیر عمل می‌کنیم:
<h2>Geolocation</h2>
<p>Latitude is {location.latitude}</p>
<p>Longitude is {location.longitude}</p>
<p>Your speed is {location.speed ? location.speed : "0"}</p>

سؤال: در اینجا شیء location چندین بار تکرار شده‌است. آیا می‌توان مقادیر خواص آن‌را توسط Object Destructuring نیز به دست آورد؟
پاسخ: بله. در اینجا هم Object Destructuring بر روی location state، کار می‌کند:
const [{ latitude, longitude, speed }, setLocation] = useState(
    initialLocationState
  );
که پس از آن، دیگر نیازی به تکرار شیء location نخواهد بود:
<h2>Geolocation</h2>
<p>Latitude is {latitude}</p>
<p>Longitude is {longitude}</p>
<p>Your speed is {speed ? speed : "0"}</p>


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-30-part-01.zip
بازخوردهای دوره
تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET MVC
«آیا این سرویس باید بصورت singleton پیاده سازی شود؟»
خیر. باید از سطح دوم کش استفاده کنید. برای مثال:
- یک نمونه پیاده سازی سطح دوم کش در EF
- استفاده از AOP برای پیاده سازی سطح دوم کش
- پروژه‌ی جدید «Second Level Cache for Entity Framework 6.1» 
مطالب
AngularJS #1
پیش از اینکه آموزش AngularJs را شروع کنیم بهتر است با مفهوم برنامه‌های تک صفحه ای وب  و یا  Single Page Web Applications آشنا شویم؛ چرا که AngularJS برای توسعه هر چه ساده‌تر و قوی‌تر این گونه برنامه‌ها متولد شده است.

Single Page Application
برای درک چگونگی کارکرد این برنامه ها، مثالی را میزنیم که هر روزه با آن سرو کار دارید، یکی از نمونه‌های کامل و قدرتمند برنامه‌های Single Page Application و یا به اختصار SPA، سرویس پست الکترونیکی Google و یا همان Gmail است.
اجازه بدهید تا ویژگی‌های SPA را با بررسی Gmail انجام دهم، تا به درک روشنی از آن برسید:

Reload نشدن صفحات
در Gmail هیچ گاه صفحه Reload و یا اصطلاحا بارگیری مجدد نمی‌شود. برای مثال، وقتی شما لیست ایمیل‌های خود را مشاهده میکنید و سپس بر روی یکی از آن‌ها کلیک میکنید، بدون اینکه به صفحه ای دیگر هدایت شوید؛ایمیل مورد نظر را میبینید. در حقیقت تمامی اطلاعات در همان صفحه نمایش داده می‌شوند و بر عکس وب سایت‌های معمول است که از صفحه ای به صفحه‌ی دیگر هدایت میشوید ، در یک صفحه تمام کارهای مورد نیاز خود را انجام می‌دهید و احتیاجی به بارگیری مجدد صفحات نیست. با توجه به این صحبت‌ها برای توسعه دهنده‌های وب آشکار است که تکنیک AJAX، نقشی اساسی در این گونه برنامه‌ها دارد، چون کلیه‌ی عملیات ارتباط با سرور در پشت زمینه انجام می‌شوند.

تغییر URL در نوار آدرس مرورگر
وقتی شما بر روی یک ایمیل کلیک می‌کنید و آن ایمیل را بدون Reload شدن مجدد صفحه مشاهده می‌کنید، آدرس صفحه در مرورگر نیز تغییر می‌کند. خب مزیت این ویژگی چیست؟ مزیت این ویژگی در این است که هر ایمیل شما دارای یک آدرس منحصر به فرد است و به شما امکان Bookmark کردن آن لینک، باز کردن آن در یک Tab جدید و یا حتی ارسال آن به دوستان خود را دارید. حتی اگر این مطلب را جدا از Gmail در نظر بگیریم، به موتور‌های جست و جو کمک می‌کند، تا هر صفحه را جداگانه Index کنند؛ جدا از اینکه وبسایت ما SPA است. همچنین این کار یک مزیت مهم دیگر نیز دارد؛ و آن کار کردن کلیدهای back و forward مرورگر، برای بازگشت به صفحات پیمایش شده قبلی است.
شاید قبل از بیان این ویژگی با خود گفته باشید که پیاده سازی Reload نشدن صفحات با AJAX آن چنان کار پیچیده ای نیست. بله درست است، اما آیا شما قبل از این راه حلی برای تغییر URL اندیشیده بودید؟ مطمئنا شما هم صفحات وب زیادی را دیده اید که همه‌ی صفحات آن دارای یک URL در نوار آدرس مرورگر هستند و هیچگاه تغییر نمی‌کنند و با باز کردن یک لینک در یک Tab جدید، باز همان صفحه‌ی تکراری را مشاهده می‌کنند! و یا بدتر از همه که دکمه‌ی back مرورگر غیر عادی عمل می‌کند. بله، اینها تنها تعدادی از صدها مشکلات رایج سیستم‌های نوشته شده ای است که سعی کردند همه‌ی کارها در یک صفحه انجام شود.

Cache شدن اطلاعات دریافتی
شاید خیلی‌ها ویژگی‌های فوق را برای یک SPA کافی بدانند، اما تعدادی هم مانند نگارنده وجود یک کمبود را حس می‌کنند و آن کش شدن اطلاعات دریافتی در مرورگر است. Gmail این امکان را به خوبی پیاده سازی کرده است. لیست ایمیل‌های دریافتی در بار اول از سرور دریافت می‌شود، سپس شما بر روی یک ایمیل کلیک و آن را مشاهده می‌کنید. حال به لیست ایمیل‌های دریافتی بازگردید، آیا رفت و برگشتی به سرور انجام می‌شود؟ مسلما خیر. حتی اگر دوباره بر روی آن ایمیل مشاهده شده ، کلیک کنید، بدون رفت و برگشتی به سرور آن ایمیل را مشاهده می‌کنید.
کش شدن اطلاعات سبب می‌شود که بار سرور خیلی کاهش یابد و رفت و آمدهای بیهوده صورت نگیرد. کش شدن داده‌ها یک مزیت دیگر نیز دارد و آن تبدیل برنامه‌های معمول وب stateless به برنامه‌های شبه دسکتاپ state full است. 
  
تکنیک AJAX در پیاده سازی امکانات فوق نقشی اساسی را بازی می‌کند. کمی به عقب برمیگردیم یعنی زمانی که AJAX برای اولین بار مطرح شد و هدف اصلی به وجود آمدن آن پیاده سازی برنامه‌های وب به شکل دسکتاپ بود و این کار از طریق انجام تمامی ارتباطات سرور با  XMLHttpRequest  امکان پذیر می‌شد. شاید آن زمان با توجه به محدودیت تکنولوژی‌ها موجود این کار به صورت تمام و کمال امکان پذیر نبود، اما امروزه به بهترین شکل ممکن قابل پیاده سازی است.

 شاید اکنون این سوال پیش بیاید که چرا باید وبسایت خود را به شکل SPA طراحی کنیم؟
برای پاسخ دادن به این سوال باید گفت که سیستم‌های وب  امروزی به دو دسته‌ی زیر تقسیم می‌شوند:
Web Documents و یا همان وب سایت‌های معمول
Web Applications و یا همان Single Page Web Applications
اگر هدف شما طراحی یک وب سایت معمول است که هدف آن، نمایش یک سری اطلاعات است و به قولی دارای محتواست، مطمئنا پیاده سازی این سیستم به صورت SPA کاری بیهوده به نظر می‌آید؛ ولی اگر هدفتان نوشتن سیستم هایی مثل Gmail، Google Maps، Azure، Facebook و ... است، پیاده سازی آن‌ها به صورت وب سایت‌های معمولی، غیر معقول به نظر می‌آید. حتی بخش‌های مدیریتی یک وبسایت هم می‌تواند به خوبی توسط SPA پیاده سازی شود، چرا که واقعا برای مدیریت اطلاعات یک وب سایت احتیاجی نیست، که  از این صفحه به آن صفحه جا به جا شد.

معرفی کتابخانه‌ی AngularJS
AngularJS فریم ورکی متن باز و نوشته شده به زبان جاوا اسکریپت است. هدف از به وجود آمدن این فریم ورک، توسعه هر چه ساده‌تر SPA‌ها با الگوی طراحی MVC و تست پذیری هر چه آسان‌تر آن‌ها است. این فریم ورک توسط یکی از محققان Google در سال 2009 به وجود آمد. بعد‌ها این فریم ورک تحت مجوز MIT به صورت متن باز در آمد و اکنون گوگل آن را حمایت می‌کند و توسط هزاران توسعه دهنده در سرتاسر دنیا، توسعه داده می‌شود.
قبل از اینکه به بررسی ویژگی‌های Angular بپردازم، بهتر است ابتدا مطلبی درباره‌ی به کارگیری Angular از Brad Green که کارمند گوگل است، بیان کنم.
در سال 2009 تیمی در گوگل مشغول انجام پروژه ای به نام Google Feedback بودند. آن‌ها سعی داشتند تا در طی چند ماه، به سرعت کد‌های خوب و تست پذیر بنویسند. پس از 6 ماه کدنویسی، نتیجه‌ی کار 17000 خط کد شد. در آن موقع یکی از اعضای تیم به نام Misko Hevery، ادعا کرد که می‌تواند کل این پروژه را در دو هفته به کمک کتابخانه‌ی متن بازی که در اوقات فراغت توسعه داده است، بازنویسی کند. Misko نتوانست در دو هفته این کار را انجام دهد. اما پس از سه هفته همه‌ی اعضای تیم را شگفت زده کرد. نتیجه‌ی کار تنها 1500 خط بود! همین باعث شد که ما بفهمیم که، Misko بر روی چیزی کاری میکند که ارزش دنبال کردن دارد.
پس از آن قضیه Misko و Brad بر روی Angular کار کردند و اکنون هم Angular توسط تیمی در گوگل و هزاران توسعه دهنده‌ی متن باز حرفه ای در سرتاسر جهان، درحال توسعه است.
فکر کنم همین داستان ذکر شده، قدرت فوق العاده زیاد این فریم ورک را برای همگان آشکار سازد.

ویژگی‌های AngularJS:

قالب‌های سمت کاربر (Client Side Templates): انگولار دارای یک template engine قدرتمند برای تعریف قالب است.

پیروی از الگوی طراحی MVC: انگولار، الگوی طراحی MVC را برای توسعه پیشنهاد می‌دهد و امکانات زیادی برای توسعه هر چه راحت‌تر با این الگو فراهم کرده است.

Data Binding: امکان تعریف انقیاد داده دوطرفه (Two-Way Data Binding) در این فریم ورک به راحتی هرچه تمام، امکان پذیر است.

Dependency Injection: این فریم ورک برای دریافت وابستگی‌های تعریف شده، دارای یک سیستم تزریق وابستگی توکار است.

تعریف Service‌های سفارشی: در این فریم ورک امکان تعریف سرویس‌های دلخواه به صورت ماژول وجود دارد. این ماژول‌های مجزا را به کمک سیستم تزریق وابستگی توکار Angular، به راحتی در هر جای برنامه می‌توان تزریق کرد.

تعریف Directive‌های سفارشی: یکی از جذاب‌ترین و قدرتمند‌ترین امکانات این فریم ورک، تعریف Directive‌های سفارشی است.  Directive ها، امکان توسعه HTML را فراهم کرده اند. توسعه‌ی HTML اکنون در قالب Web Components‌ها فراهم شده است، اما هنوز هم خیلی از مرورگر‌های جدید نیز از آن پشتیبانی نمی‌کنند.

فرمت کردن اطلاعات با استفاده از فیلترهای سفارشی: با استفاده از فیلترها میتوانید چگونگی الحاق شدن اطلاعات را برای نمایش به کاربر تایین کنید ؛ انگولار همراه با فیلترهای گوناگون مختلفی عرضه میشود که میتوان برایه مثال به فیلتر currency ، date ،uppercase کردن رشته‌ها و .... اشاره کرد همچنین شما محدود به فیلترهای تعریف شده در انگولار نیستید و آزادید که فیلترهای سفارشی خودتان را نیز تعریف کنید.

سیستم Routing: دارا بودن سیستم Routing  قدرتمند، توسعه SPA‌ها را بسیار ساده کرده است.

سیستم اعتبار سنجی: Angular دارای سیستم اعتبار سنجی توکار قدرتمند برای بررسی داده‌های ورودی است.

سرویس تو کار برای ارتباط با سرور: Angular دارای سرویس پیش فرض ارتباط با سرور به صورت AJAX است.

تست پذیری: Angular دارای بستری آماده برای تست کردن برنامه‌های نوشته شده است و از Unit Tests و Integrated End-to-End Test هم پشتیبانی می‌کند.

جامعه‌ی متن باز بسیار قوی
   
این‌ها فقط یک مرور کلی بر توانایی‌های این فریم ورک بود و در ادامه هر کدام از این ویژگی را به صورت دقیق بررسی خواهیم کرد.
در مقاله‌ی بعدی، به چگونگی نصب AngularJS خواهیم پرداخت. سپس، اولین کد خود را با استفاده از آن خواهیم نوشت و مطالب Client Side Templates و MVC را دقیق‌تر بررسی خواهیم کرد.
 
نظرات مطالب
تاریخ شمسی برای blogger !
- ساعت کامنت‌ها رو درست کردم. اسکریپت farsidate.js و متن ارسالی فوق به روز شد.
- در مورد حجم، چون این اسکریپت‌های لینک داده شده توسط مرورگر کش خواهند شد، فقط بار اول 50 کیلوبایت سربار دارد. در دفعه بعد بازدید، از کش خوانده می‌شود.
مطالب
OpenCVSharp #16
در قسمت قبل با نحوه‌ی استفاده از یک trained data از پیش آماده شده‌ی تشخیص چهره‌، آشنا شدیم. در این قسمت قصد داریم با نحوه‌ی تولید این فایل‌های XML آشنا شویم و یک تشخیص دهنده‌ی سفارشی را طراحی کنیم.


طراحی classifier سفارشی تشخیص خودروها

برای طراحی یک تشخیص دهنده‌ی سفارشی مبتنی بر الگوریتم‌های Machine learning، نیاز به تعداد زیادی تصویر داریم. در اینجا از بانک تصاویر خودروهای «UIUC Image Database for Car Detection» استفاده خواهیم کرد. در  این بسته، یک سری تصویر positive و negative را می‌توان ملاحظه کرد. تصاویر مثبت، تصاویر انواع و اقسام خودروها هستند (550 عدد) و تصاویر منفی، تصاویر غیر خودرویی (500 عدد)؛ یا به عبارتی، هر تصویری، منهای تصاویر خودرو می‌تواند تصویر منفی باشد.


ایجاد فایل برداری از تصاویر خودروها

در ادامه یک فایل متنی را به نام carImages.txt ایجاد می‌کنیم. هر سطر این فایل چنین فرمتی را خواهد داشت:
 pos/pos-177.pgm 1 0 0 100 40
ابتدا مسیر تصویر مشخص می‌شود. سپس عدد 1 به این معنا است که در این تصویر فقط یک عدد خودرو وجود دارد. 4 عدد بعدی، ابعاد مستطیلی تصویر هستند.
در ادامه فایل متنی دیگری را به نام negativeImages.txt جهت درج اطلاعات تصاویر منفی، ایجاد می‌کنیم. اینبار هر سطر این فایل تنها حاوی مسیر تصویر مدنظر است:
 neg/neg-274.pgm
این دو فایل را می‌توان با استفاده از دو متد ذیل، به سرعت تولید کرد:
private static void createCarImagesFile()
{
    var sb = new StringBuilder();
    foreach (var file in new DirectoryInfo(@"..\..\CarData\CarData\TrainImages").GetFiles("*pos-*.pgm"))
    {
        sb.AppendFormat("{0} {1} {2} {3} {4} {5}{6}", file.FullName, 1, 0, 0, 100, 40, Environment.NewLine);
    }
    File.WriteAllText(@"..\..\CarsInfo\carImages.txt", sb.ToString());
}
 
private static void createNegativeImagesFile()
{
    var sb = new StringBuilder();
    foreach (var file in new DirectoryInfo(@"..\..\CarData\CarData\TrainImages").GetFiles("*neg-*.pgm"))
    {
        sb.AppendFormat("{0}{1}", file.FullName,Environment.NewLine);
    }
    File.WriteAllText(@"..\..\CarsInfo\negativeImages.txt", sb.ToString());
}
برای کامپایل اطلاعات فایل‌های تولید شده، نیاز به فایل opencv_createsamples.exe است. این فایل را در پوشه‌ی opencv\build\x86\vc12\bin بسته‌ی اصلی OpenCV می‌توانید پیدا کنید.
 opencv_createsamples.exe -info carImages.txt -num 550 -w 48 -h 24 -vec cars.vec
پارامترهای این دستور شامل سوئیچ info است؛ به معنای مشخص سازی فایل اطلاعات تصاویر مثبت. سوئیچ num تعداد تصاویر آن‌را تعیین می‌کند و سوئیچ‌های w و h، طول و عرض تصاویر هستند. سوئیچ vec نیز جهت تولید یک فایل vector از این اطلاعات بکار می‌رود.
پس از اجرای این دستور، فایل cars.vec تولید خواهد شد؛ با این خروجی:
 Info file name: carImages.txt
Img file name: (NULL)
Vec file name: cars.vec
BG  file name: (NULL)
Num: 550
BG color: 0
BG threshold: 80
Invert: FALSE
Max intensity deviation: 40
Max x angle: 1.1
Max y angle: 1.1
Max z angle: 0.5
Show samples: FALSE
Original image will be scaled to:
  Width: $backgroundWidth / 48
  Height: $backgroundHeight / 24
Create training samples from images collection...
Done. Created 550 samples
اگر علاقمند هستید که محتویات فایل باینری cars.vec را مشاهده کنید، دستور ذیل را صادر نمائید:
 "c:\opencv\build\x86\vc12\bin\opencv_createsamples.exe" -vec cars.vec -w 48 -h 24


در این پنجره‌ی باز شده، تصاویر بعدی و قبلی را می‌توان با دکمه‌های arrow صفحه کلید، مشاهده کرد.


تبدیل فایل برداری تصاویر خودروها به trained data

تا اینجا موفق شدیم بیش از 500 تصویر خودرو را تبدیل به یک فایل برداری سازگار با OpenCV کنیم. اکنون نیاز است، این اطلاعات پردازش شده و trained data مخصوص الگوریتم‌های machine learning تولید شود. این‌کار را توسط برنامه‌ی opencv_traincascade.exe انجام خواهیم داد. این فایل نیز در پوشه‌ی opencv\build\x86\vc12\bin بسته‌ی اصلی OpenCV موجود است.
دستور ذیل در پوشه‌ی data، بر اساس اطلاعات برداری cars.vec و همچنین تصاویر منفی مشخص شده‌ی در فایل negativeImages.txt، با تعداد هر کدام 500 عدد (این عدد را توصیه شده‌است که اندکی کمتر از تعداد max موجود مشخص کنیم) و تعداد مراحل 2  (هر چقدر این تعداد مراحل بیشتر باشد، فایل نهایی تولید شده دقت بالاتری خواهد داشت؛ اما تولید آن به زمان بیشتری نیاز دارد) اجرا می‌شود. در اینجا featureType به LBP یا Local binary Pattern، تنظیم شده‌است. این الگوریتم از Haar cascade سریعتر است.
 "E:\opencv\bin\opencv_traincascade.exe" -data data -vec cars.vec -bg negativeImages.txt -numPos 500 -numNeg 500 -numStages 2 -w 48 -h 24 -featureType LBP
خروجی اجرای این دستور را می‌توانید در پوشه‌ی data با نام cascade.xml، پیدا کنید. پس از آن، روش استفاده‌ی از این فایل، با مطلب تشخیص چهره تفاوتی ندارد.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
مطالب
تشخیص نقایص تصاویر صفحات سایت با استفاده از jQuery Ajax

این مثال شبیه به مثال بررسی وجود نام کاربر با استفاده از jQuery Ajax است که از ذکر توضیحات مشابه آن، در اینجا خودداری خواهد شد.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestBrokenImages.aspx.cs"
Inherits="testWebForms87.TestBrokenImages" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>detecting broken images</title>

<script src="jquery.min.js" type="text/javascript"></script>

<script type="text/javascript">
function errorReplace(arg) {
//ارسال پیغام خطا
$.ajax({
type: "POST",
url: "TestBrokenImages.aspx/GetErros",
data: "{'image': '" + arg.src + "','page':'" + location.href + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json"
});
//نمایش تصویری دلخواه بجای نمونه مفقود
$(arg).attr('src', 'missing.png');
}

//بررسی وضعیت تک تک تصاویر پس از بارگذاری کامل صفحه
$(document).ready(function() {
$(window).bind('load', function() {
$('img').each(function() {
if (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0))) {
errorReplace(this);
}
});
})
});
</script>

</head>
<body>
<form id="form1" runat="server">
<div>
<img src="img1.png" />
<img src="img2.png" />
</div>
</form>
</body>
</html>

using System;
using System.IO;
using System.Web.Services;

namespace testWebForms87
{
public partial class TestBrokenImages : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}

[WebMethod]
public static void GetErros(string image,string page)
{
//ارسال ایمیل به مسؤول سایت و یا ذخیره خطاها در دیتابیس
}
}
}

در این مثال زمانیکه صفحه کاملا بارگذاری شد، وضعیت تک تک تصاویر بررسی می‌شود، اگر تصویر مفقودی وجود داشت (با اکثر مرورگرها سازگار است)، اطلاعات آن به تابع errorReplace ارسال خواهد شد.
در این تابع با استفاده از jQuery Ajax ، اطلاعات تصویر مفقود و صفحه مربوطه به وب متد GetErros ما ارسال می‌شود. سپس در این متد می‌توان یا آرگومان‌های دریافتی را به صورت یک ایمیل به مسؤول سایت ارسال نمود و یا آن‌ها را جهت بررسی آتی در یک دیتابیس ذخیره کرد.
بدیهی است بجای قرار دادن وب متد فوق در صفحه جاری، می‌توان یک وب سرویس را نیز ایجاد و متد را در آن قرار داد تا نیازی نباشد به ازای هر صفحه سایت یکبار این متد تکرار شود.

اگر موفق به اجرای این مثال نشدید، برای مثال یک break point داخل متد GetErrors قرار دهید و برنامه را در حالت دیباگ در ویژوال استودیو شروع کنید، اگر اتفاق خاصی رخ نداد و به این break point نرسیدید، احتمالا تنظیمات وب کانفیگ شما مناسب نیست. قسمت مربوط به system.web.extensions ، webServices و jsonSerialization باید در وب کانفیگ موجود باشد که VS 2008 این موارد را به صورت خودکار اضافه می‌کند.