I am sure most programmers have heard of Node.js, but what aboutNodeOS? Yes, NodeOS, an operating system written in Node.js. Well, kind of. NodeOS uses the Linux kernel for most performance critical stuff like, for example, hardware interactions, but for everything else it uses Node.js. NodeOS development started two years ago and was created by people who shared a simple, but intriguing, idea: “Is it possible to create an operating system using only Node.js
Once created, these custom elements -- a custom counter, for example -- can also be used in other single-page application (SPA) web frameworks such as React and Angular. A sample project, aptly titled Blazor Custom Elements, shows how to do just that, providing examples about how to work with those frameworks and the client-side Blazor WebAssembly component as well as Blazor Server.
- VS2019 Preview 4 broke conditional compilation symbols in C#
- API has been deprecated
- Android debugging not working in latest release 16.4 "Debuggee returned error code INVALID_ARGUMENT"
- Fixed a frequent UI delay that can occur while using the editor.
- Fixed a crash when an attribute constructor is decorated with itself and Nullable Reference Types is enabled while using C#.
- Fixed an issue that resulted in Visual Studio failing to start or crashing on C# solution load on certain Virtual Machine setups.
- Fixed a bug where user defined conditional compliation symbols are lost when the build properties page is reopened.
بررسی دلایل انتخاب Angular
ندامتنامهی یک برنامه نویس
برپایی پیشنیازها
در اینجا برای بررسی React Hooks، یک پروژهی جدید React را ایجاد میکنیم:
> npm i -g create-react-app > create-react-app sample-30 > cd sample-30 > npm start
> npm install --save bootstrap
import "bootstrap/dist/css/bootstrap.css";
همچنین اگر به فایل 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;
اکنون میخواهیم همین کامپوننت را توسط React hooks بازنویسی کنیم. در ابتدا، فایل app.js را به AppClass.js، تغییر نام میدهیم تا نگارش قبلی class component را برای مقایسه، در اختیار داشته باشیم. در ادامه فایل جدید AppFunction.js را برای بازنویسی آن توسط یک کامپوننت تابعی، توسط میانبرهای imrc و سپس sfc در VSCode، ایجاد میکنیم. البته این تغییر نام فایلها، نیاز به تغییر نام ماژولهای import شدهی در فایل index.js را نیز به صورت زیر دارد:
//import App from "./AppClass"; import App from "./AppFunction";
اولین سؤالی که اینجا مطرح میشود، این است: در این کامپوننت تابعی جدید، state را از کجا بدست بیاوریم؟
با React Hooks، بجای تعریف یک state به صورت خاصیت، آنرا صرفا use میکنیم و این دقیقا نام اولین React Hooks ای است که بررسی میکنیم؛ یا همان useState. بنابراین ابتدا این شیء را import خواهیم کرد:
import React, { useState } from 'react';
const App = () => { const [count, setCount] = useState(0);
useState<number>(initialState: number | (() => number)): [number, React.Dispatch<React.SetStateAction<number>>]
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;
- همچنین در اینجا (داخل این متد) دیگر خبری از 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 }); };
incrementCount = () => { this.setState(prevState => ({ count: prevState.count + 1 })); };
این نکته در مورد کامپوننتهای تابعی نیز وجود دارد:
const incrementCount = () => { setCount(count + 1); };
const incrementCount = () => { setCount(prevCount => prevCount + 1); };
به روز رسانی بیش از یک خاصیت در 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، از ماژول AppClass تامین شود:
import App from "./AppClass"; //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، از ماژول AppFunction تامین شود:
// import App from "./AppClass"; 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";
در اینجا 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`; });
در اولین بار اجرای برنامه، عبارت «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`; }
- همچنین چون میخواهیم به ازای هر تغییری در state نیز این عنوان تغییر کند (با هر بار کلیک بر روی دکمه)، باید از متد componentDidUpdate هم استفاده کنیم.
پاکسازی اثرات جانبی در useEffect Hook
فرض کنید قصد داریم موقعیت فعلی کرسر ماوس را در مرورگر نمایش دهیم. برای انجام اینکار در کامپوننت کلاسی، میتوان از متد componentDidMount جهت دسترسی به DOM API و استفاده از متد addEventListener آن، برای گوش فرا دادن به حرکات کرسر ماوس، استفاده کرد:
class App extends Component { componentDidMount() { // ... window.addEventListener("mousemove", this.handleMouseMove); }
handleMouseMove = event => { this.setState({ x: event.pageX, y: event.pageY }); };
class App extends Component { state = { //... x: 0, y: 0 };
<h2>Mouse Position</h2> <p>X position: {this.state.x}</p> <p>Y position: {this.state.y}</p>
- تعدادی از آنها نیازی به پاکسازی و خارج شدن از حافظه را ندارند؛ مانند به روز رسانی عنوان صفحه در مرورگر. میتوان یک چنین 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 });
سپس 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 }); };
سپس برای نمایش x و y به روز رسانی شدهی در state، میتوان از markup زیر در متد رندر استفاده کرد.
<h2>Mouse Position</h2> {JSON.stringify(mousePosition, null, 2)} <br />
اگر effect function تعریف شده، دارای یک خروجی (از نوع تابع) باشد، به این معنا است که این side effect، نیاز به پاکسازی دارد و این متد را در زمان Unmount آن فراخوانی میکند:
useEffect(() => { // … // componentDidMount & componentDidUpdate window.addEventListener("mousemove", handleMouseMove); // componentWillUnmount return () => { window.removeEventListener("mousemove", handleMouseMove); }; });
سؤال: اگر بخواهیم از اجرای یک side effect، به ازای هر بار رندر جلوگیری کنیم، چه باید کرد؟
برای اینکار میتوان آرگومان دومی را به متد useEffect اضافه کرد که آرایهای از مقادیر است. توسط اعضای آن میتوان مقدار و یا مقادیری را مشخص کرد که side effect تعریف شده، به آن وابستهاست. اکنون اگر این مقدار تغییر کند، آنگاه side effect متناظر با آن نیز اجرا میشود:
useEffect(() => { document.title = `You have clicked ${count} times`; window.addEventListener("mousemove", handleMouseMove); return () => { window.removeEventListener("mousemove", handleMouseMove); }; },[]);
برای رفع این مشکل، باید به useEffect اعلام کنیم که side effect تعریف شدهی در آن، وابستهاست به مقدار count و با تغییرات آن در state، نیاز است مجددا اجرا شود:
useEffect(() => { // ... },[count]);
کار با چندین listener مختلف در متد useEffect
سؤال: آیا تنظیم یک وابستگی خاص در متد useEffect، امکان تنظیم event listenerهای دیگر را غیرممکن میکند؟
برای پاسخ به این سؤال، چند event listener دیگر را ثبت میکنیم. برای مثال یکی دیگر از APIهای مرورگر، navigator نام دارد که توسط آن میتوان وضعیت آنلاین و آفلاین بودن را به کمک خروجی خاصیت navigator.onLine آن، مشخص کرد. به کمک این API میخواهیم این وضعیت را نمایش دهیم. برای این منظور ابتدا state آنرا در کامپوننت تابعی، ایجاد میکنیم:
const [status, setStatus] = useState(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]);
const handleOnline = () => { setStatus(true); }; const handleOffline = () => { setStatus(false); };
<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);
useEffect(() => { // ... navigator.geolocation.getCurrentPosition(handleGeolocation); const watchId = navigator.geolocation.watchPosition(handleGeolocation); return () => { // ... navigator.geolocation.clearWatch(watchId); }; }, [count]);
یک روش برای حل این مشکل و غیرفعال کردن دستی listener متد getCurrentPosition پس از unmount، تعریف یک متغیر mounted پیش از متد useEffect است:
let mounted = true; useEffect(() => { // ... return () => { // ... mounted = false; }; }, [count]);
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 );
<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
Based on the above observations, we can conclude that React will be the best framework to learn in 2021, followed by Vue. But there is a high chance of Angular defending second place since it has been there for a longer period of time than Vue, and surely 2021 is not the end of that. So if you are an Angular developer, I suggest you learn React in the upcoming days.