برنامه نویس وب و فرانتاند و ریاکت و وب سایت https://react.ir
خداحافظ ریداکس، سلام کانتکست (قسمت دوم)
تو قسمت قبلی درباره ویژگیهای صحبت کردیم تو این قسمت یکم مسائل رو بیشتر باز میکنم ...
اگر قسمت اول رو مطالعه نکردید از لینک زیر به اون دسترسی دارید:
شاید برای شما هم سوال باشه که چرا این مقاله رو دو قسمت کردم ؟
خب دلیلش این بود که میخواستم نظر شما رو درباره کانتکست بدونم، سوالاتتون رو بشنوم و اینجا تو قسمت دوم بهشون جواب بدم. (اگر از گوشی موبایل دارید این مقاله میخونید و لینک رو از تلگرام باز کردید، مطمئن بشید که تو حالت instant view نیستید چون قسمتهایی از مقاله تو اون حالت نمایش داده نمیشه نرم افزار گذر از تحریم هم داشته باشید بد نیست :)))) )
کمی ریاکت !
۱) همونطور که همه میدونید برای آپدیت استیت کافیه متد setState رو صدا کنیم.
class Foo extends React.Component {
state = {
count: 1
}
handle = () => {
this.setState({ count: this.state.count +1 })
}
render() {
return <button ={this.handle}>{count}</button>
}
}
ولی خب کد بالا مشکل اساسی داره اونم اینکه استیت رو داره بر اساس مقدار فعلی آپدیت میکنه!
برای آپدیت استیت نسبت به استیت قبلی بهتره که از callback استفاده کنیم. با استفاده از callback برنامه ما optimize میشه، اینطوری ریکت میتونه به صورت یک دفعه (batch) مقدار استیت رو تغییر بده. کار با callback خیلی ساده هستش، کافیه متد handle بالا رو به صورت زیر تغییر بدیم:
handle = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }))
}
برای فانکشن کامپوننت و هوک useState هم باید همین کار رو انجام داد.
۲) هوک useState معادل state در کلاس کامپوننتها نیست
class Foo extends React.Component {
state = {
name: "nim nim",
age: 21
}
handle = () => {
this.setState({ name: "Nima" })
}
render() {
return <button ={this.handle}>Fix Name</button>
}
}
با توجه به کد بالا اگر روی دکمه FixName کلیک کنیم، نام از nim nim به nima تغییر پیدا میکنه. و آبجکت استیت ما برابر میشه با:
{
name: "nima",
age: 21
}
و اگر کامپوننت بالا رو با فانکشن کامپوننت و هوک بازنویسی کنیم:
function Foo() {
const [state, setState] = React.useState({ name: "nim nim", age: 21 })
function handle() {
setState({ name: "Nima" })
}
return <button ={handle}>Fix Name</button>
}
با کلیک بر روی دکمه FixName مقدار استیت برابر میشه با:
{
name: "nima"
}
اگر دقت کرده باشید مقدار age از استیت حذف شده، این تفاوتی هستش که اول درباره اون صحبت کردم، برای حل این مسئله باید از callback استفاده کنید و استیت قبلی رو به استیت جدید اضافه کنید
function handle() {
setState((prevState) => ({ ...prevState, name: "Nima" }))
}
۳) مقدار اولیه useState ؟
function Bar() {
const [state, setState] = React.useState(0)
}
خب تو مثال بالا مقدار اولیه state برابر هستش با عدد ۰، ولی اگر برای محاسبه مقدار اولیه قرار بود یه کار عجیب انجام بدیم چی ؟ مثلا مقدار اولیه استیت نتیجه اجرای یک متد باشه که عملیات پردازشی سنگینی داره و زمان میبره تا انجام بشه.
function Bar() {
const initalState = veryLongTask()
const [state, setState] = React.useState(initalState)
}
خب مشکل ما یکبار اجرا شدن veryLongTask نیست، موضوع این هستش که هربار که کامپوننت ریرندر میشه اون متد باید صدا زده بشه نتیجه اون محاسبه بشه و در آخر مقدارش به درد ما نمیخوره چون استیت فقط یکبار مقدار دهی میشه و از رندر دفعه دوم به بعد دیگه کاری به پارامتر اولی که بهش دادیم نداره و صرفا زمان CPU رو مصرف کردیم. برای حل این مشکل تیم ریاکت قابلیتی به ما دادن که این اتفاق نیوفته. کافیه به useState یک متد به صورت callback پاس بدیم و ریاکت اون متد رو فقط یکبار صدا میزنه و مقدارشو به عنوان استیت اولیه قرار میده
const [state, setState] = React.useState(() => veryLongTask())
که البته اگر بخوایم خیلی خفن طوری کد بزنیم میشه:
const [state, setState] = React.useState(veryLongTask)
با این تغییرات متد veryLongTask فقط یکبار صدا زده میشه.
۴) ریاکت باهوش هستش
برای جابهجایی دیتا بین کامپوننتها ما از پراپ استفاده میکنیم، تو دنیای ریاکت ما ۲ تا تا پراپ داریم که شرایطشون با بقییه پراپها فرق داره داره. پراپ key و پراپ children.
به کد زیر دقت کنید:
// MyComponent.js
function MyComponent({ children }) {
const [ count, setCount ] = React.useState(0)
const handle = () => setCount(prevCount => prevCount + 1)
return (
<>
<button ={handle}>
{count}
</button>
{children}
</>
)
}
// ============
// App.js
function App() {
return (
<MyComponent>
<p>I'm Nima Arefi</p> // <=== this is children
<MyComponent>
)
}
اگر دقت کرده باشید بین تگ باز و تگ بسته کامپوننت MyComponent کامپوننتهای دیگهایی رندر شدن و داخل MyComponent از طریق پراپ children کامپوننت هایی که بین تگ باز و بسته بودن رو رو صفحه نمایش دادیم. (تو این مثال مقدار children برابر هستش با <p>I'm Nima Arefi</p>)
اینجا هستش که ریاکت شروع میکنه به optimize کردن children. اگر ما روی دکمهای که داخل MyComponent هستش کلیک کنیم و کامپوننت ریرندر بشه، اتفاقی برای کامپوننت <p>I'm Nima Arefi</p> که داخل children بود نمیافته و اون دوباره رندر نمیشه :)
بله ریاکت به قدری باهوش هستش که جلوی رندر شدن بیمورد رو بگیره.
کمی جاوااسکریپت!
۱) ۲ آبجکت با خواص یکسان باهم برابر نیستند!
{} == {} // false
{ name: "nima" } == { name: "nima" } // false
const foo = { nickName: "nim nim" }
const bar = { nickName: "nim nim" }
bar == foo // false
bar.nickName == foo.nickName // true <=== WOW
۲) متدها متد میسازند!
function foo() {
function bar() {
console.log("hi i'm bar")
}
function baz() {
console.log("hi i'm baz")
}
return { baz, bar }
}
const { baz, bar } = foo()
baz() // hi i'm baz
bar() // hi i'm bar
و جالب تر اینکه scope خودشونو دارن
function foo() {
const data = { name: "Nima" }
function bar() {
console.log("hi i'm bar")
console.log("And data is " + data.name)
}
function baz() {
console.log("hi i'm baz")
}
return { baz, bar }
}
const { baz, bar } = foo()
baz() // hi i'm baz
bar() // hi i'm bar
// and data is Nima
کمی الگوریتم!
گاهی برای به خاطر سپردن یک مقدار لازم هستش اون مقدار رو Memorize کنیم
var memorizationFunc = function(func) {
let cached = {}; // hash table to store the intermediate values
return function() {
let key = [].slice.call(arguments, 0); // convert arguments to array.
if (cached[key]) { // has this been calculated before?
return cached[key]; // return the value so we don't need to compute it
}
let val = func.apply(this, arguments); // call the function with arguments
cached[key] = val; // save the value to the cached table
return val;
}
}
کاری که این الگوریتم میکنه خیلی ساده هستش فرض کنید متدی داریم به نام isPrime کار این متد این هستش که به ما میگه عدد اول هستش یا نه
function isPrime(num) {
for(let i = 2; i < num; i++)
if(num % i === 0) return false;
return num > 1;
}
خب کار با این متد خیلی ساده هستش، کافیه عدد رو بهش بدید تا نتیجه رو به شما برگردونه
isPrime(2) // true
حالا شما فرض کنید این متد قراره یه کاره خیلی خیلی خیلی سنگین رو انجام بده مثلا پیدا کردن عدد اول ۵۰ رقمی (کلا هرچیزی که چند ثانیه وقت CPU رو بگیره) و چندین بارم ممکنه که بخواد اینکارو واسه یک عدد تکراری انجام بده.
فهمیدن اینکه یه عدد اول هستش یا نه خیلی سخته، چون عدد اول به جز خودش و عدد یک به هیچ عدد دیگهایی بخش پذیر نیست، و این یعنی عدد ورودی رو باید باید بر تمام اعداد طبیعی قبل از خودش (به جز ۱) تقسیم کنیم ببنیم نتیجه صفر میشه یا نه! اگر نتیجه تقسیم صفر شد یعنی اون عدد بخش پذیر بوده و عدد اول نیست (کل قضیه بیت کوین و ارز مجازی همینه، پیدا کردن یه عدد اول خیلی بزرگ که میتونه بیت کوین باشه ...)
اگر کامپوننت ریاکتی داشته باشیم که قرار باشه اینکارو انجام بده چی ؟ (با فرض اینکه کامپوننت پدر این کامپوننت همش درحال ریرندر شدن هستش و این کامپوننتم در نتیجه ریرندر میشه با این که هیچ پراپی نداره)
function MyComponent() {
const [state, setState] = React.useState(1)
const result = isPrime(state)
return (
<>
<input value={state} ={event => setState(event.target.value} />
<h1>number {state} is { result === true ? "" : "not" } prime</h1>
</>
)
}
اینجاست که ما میایم memorize میکنیم یا به فارسی سخت به خاطر میسپاریم نتیجه اجرا شدن متد isPrime رو با همون الگوریتم memroize که کدش رو بالاتر گذاشتم.
کار این الگوریتم خیلی ساده هستش هردفعه که متد صدا زده میشه ورودی های اونو داخل یه متغیر ذخیره میکنه:
let key = [].slice.call(arguments, 0);
آبجکت arguments تو هر فانکشنی هستش و داخل اون پارامترهایی هستن که ما با اون متد رو صدا کردیم، مثلا با نوشتن کد `arguments[0]` شما به پارمتر اول فانکشن دسترسی دارید.
این ورودیها رو به عنوان key آبجکت ذخیره میکنه
let cached = {}
cached[key] = WHAT ?
خب حالا باید WHAT رو مقدار دهی کنیم و ساده هستش نتیجه اجرا شدن متد ورودی به memorize رو باید داخلش بزاریم، اگر کمی گیج شدید نگران نباشید ما متد memorize رو اینطوری صدا زدیم:
const memorizedIsPrime = memorizationFunc(isPrime)
memorizedIsPrime(1)
پس ما به isPrime داخل memorizationFunc دسترسی داریم، اگر کمی اسکرول کنید و به بالا برید و کد الگوریتم memorize رو ببینید متوجه میشید که پارامتر اول این متد اسمش func هستش که اشاره گر به متد isPrime داخل اون ذخیره شده. پس کافیه func رو صدا کنیم.
و در آخر نتیجه اون رو return کنیم
var val = func.apply(this, arguments);
cached[key] = val;
return val;
و درآخر یک شرط میزاریم که چک کنیم آیا با این پارامترهایی که متد رو باهاش صدا زدیم آیا قبلا متد صدا زده شده ؟ اگر صدا زده شده بود پس نتیجه رو از cache برمیگردونم و دیگه func رو صدا نمیکنیم
if (cached[key]) {
return cached[key];
} else {
var val = func.apply(this, arguments);
cached[key] = val;
return val;
}
خب این خیلی خوبه و ریاکت هم برای ما متد useMemo رو قرار داده تا بتونم ازش هرجا که لازم بود استفاده کنیم
function MyComponent() {
const [state, setState] = React.useState(1)
const result = React.useMemo(() => isPrime(state), [state])
return (
<>
<input value={state} ={event => setState(event.target.value} />
<h1>number {state} is { result === true ? "" : "not" } prime</h1>
</>
)
}
و خب اینطوری اگر ما isPrime رو با یک عدد بزرگ تست کنیم (لطفا از عدد 1000004249 برای تست استفاده کنید)، دفعه اول شاید ۴ تا ۵ ثانیه زمان ببره ولی خب دفعه دوم و الی آخر زمان محاسبه میشه یک عدد نزدیک به ۰.
این الگوریتم خیلی خوبه ولی یه مشکل داره، اونم اینکه باید نتیجه محاسبه رو یه جا ذخیره کنه که این یعنی برنامه شما حافظه بیشتری مصرف میکنه هرچی برنامه شما بیشتر حافظه مصرف کنه کندتر میشه. خلاصه اینکه هرجایی نباید React.useMemo و React.useCallback و React.memo رو استفاده کنید ...
البته خب شاید به خودتون بگید که چه ربطی به کانتکست داشت این قضیه، که جلوتر بهش میرسیم.
قبل از شروع به کار یه مثال از کانتکس آماده کردم که میتونید از لینک زیر به کد اون دسترسی پیدا کنید:
https://codesandbox.io/s/simple-counter-with-context-bf40l?fontsize=14
برای درک این قضیه مثالی از حالتی که از ۱ کانتکست استفاده شده آماده کردم که از لینک زیر بهش دسترسی دارید:
https://codesandbox.io/s/simple-counter-with-context-7g0yq?fontsize=14
کد کامپوننت CounterProvider تو حالتی که فقط ۱ کانتکست داریم به صورت زیر میشه:
اگر دقت کرده باشید ما اینجا کار خیلی بدی کردیم :)
value={{ state, setState }}
اگر اسکرول کنید و قسمت کمی جاوااسکریپت رو از اول بخونید متوجه میشید که ۲ آبجکت حتی اگر کاملا شبیه به هم باشند و مقادیر یکسان داشته باشند باهم برابر نیستند، از طرفی زمانی که ما useContext استفاده میکنیم نیاز داریم هروقت که مقدار Context عوض شد کامپوننت ما دوباره رندر بشه تا اطلاعات جدید رو نمایش بده. خب این useContext مقدار قبلی که داخل کانتکست بود رو با مقدار جدیدی که داخل هر رندر از کامپوننت Provider میگیره مقایسه میکنه اگر باهم فرق داشته باشن کامپوننتهای متصل به اون context دوباره رندر میشن، علاوه براین آبجکتی که ساخته میشه هردفعه با نمونه قبلی فرق داره (چون مقدار state عوض شده و بار اول ۰ بوده و بار دوم ۱ و ...)
برای درک بهتر این موضوع بهتره روی دکمه Open in Editor هر ۲ مثال کلیک کنید. و کمی صبر کنید تا صفحه لود بشه و بعدش بر روی دکمه Add one چند بار کلیک کنید. در قسمت پایین و سمت راست باید این منو رو ببینید:
روی گزینه Console کلیک کنید تا منوی مربوطه باز بشه و هر بار که روی Add one کلیک کنید میبینید که مینویسه کدوم یکی از کامپوننتها رندر شده.
تو مثال اول که ۲ تا کانتکست داشت، با هر بار کلیک روی دکمه Add One فقط MyComponent رندر مجدد میشه ولی تو مثال دوم میبینید که هر کامپوننتی که به کانتکست متصل باشه دوباره رندر میشه.
اگر بخوام مثال واقعی از این حالت بگم میشه سبد خرید و دکمه افزودن به سبد خرید، ما نیازی نداریم که بعد از کلیک بر روی دکمه افزودن به سبد خرید کامپوننتی که دکمه افزودن سبد خرید رو داره دوباره رندر بشه.
خب پس دلیلش مشخص شد، در یک کلمه بخوام بگم پرفورمنس. (اینکه طوری مقاله رو بنویسم و تدریس کنم که به صورت پیش فرض کد پرفورمنسش بالا باشه چند تا لایک داره ؟)
از طرفی اگر کد کامپوننت AddButton رو ببینید متوجه میشید چرا اول مقاله به callback اشاره کردم :) اگر callback نبود ما باید کامپوننت AddButton رو هربار رندر میکردیم تا به آخرین مقدار count دسترسی پیدا کنیم و داخل از اون استفاده میکردیم.
اگر متوجه منظور من شده باشید یه لبخند ملیح باید رو صورتتون باشه و تو دلتون بگید مگه داریم مگه میشه)
اینا همه به این علت هستش که کانتکست از گوشت و استخون ریکت هستش و با API های ریاکت مچ میشه و کد نویسی با ریاکت رو ۱۰۰۰ بار جذاب تر میکنه.
همونطور که تو قسمت قبلی قول دادم قرار شد که تو این قسمت درباره Action Creator ها و Middle-ware صحبت کنم.
سازنده اکشن یا اکشن سازنده (Action Creator)
ابتدای مقاله تو قسمت کمی جاوااسکریپت گفتیم که متدها متد میسازن فراموش که نکردید؟
اگر یادتون باشه تو قسمت اول مقاله و داخل مثالی که یکم بالاتر زدیم ۲ تا کاستوم هوک ساختیم برای ارتباط با کانتکتست، یکی برای دسترسی به state و یکی دیگه برای setState.
برای ساخت Action Creator کافیه کد کاستوم هوکی که setState رو داخل خودش داره رو یکم تغییر بدیم:
و هرجا که لازم بود از اکشنها استفاده کنیم کافیه به صورت زیر عمل کنیم:
میدل ور (Middle-ware)
خب اینجا قضیه یکم فرق داره، ما میدلور نداریم ولی ابزاری ۱۰ برابر قدرتمندتر داریم به نام Hook =)
اول از همه بگم که شما میتونید به بیش از ۱۰۰۰ تا هوک آماده داخل سایت usehook.com دسترسی پیدا کنید
برسیم به اصل ماجرا، تو این بازه زمانی که بین قسمت اول و دوم بود بعضی از دوستان ریداکس کار اشاره کردن که ما با نوشتن ۳ خط کد استیت ریداکس رو ذخیره میکنیم ولی تو کانتکست اصلا این نمیشه و ...
فکر میکنید ذخیره کردن استیت کار سخیه ؟ پس این تکه کد رو ببینید:
فقط با اضافه کردن ۳،۴ خط کد به Provider استیت اون ذخیره شد، برای مطمئن شدن از این قضیه کافیه کد زیر رو تست کنید و برای رفرش شدن صفحه رو دکمهای که د رقسمت وسط و بالای صفحه قرار داره کلیک کنید
برای تمیز شدن کد Provider و امکان استفاده مجدد از این قطعه کد اون رو هوک میکنیم:
به همین راحتی :)
برای هرچیزی که فکر کنید کاستوم هوک وجود داره، اگر کاستوم هوک آمادهای برای کار شما نبود خودتون یدونه بسازید کار سختی نیست.
مثلا برای سینک شدن استیت بین تبهای مرورگر کافیه از Broadcast Api استفاده کنید که اونم کلا ۵،۶ خط کد نویسی داره.
برای انجام تسک async (مثلا data fetching یا ajax زدن) نیازی به کاستوم هوک یا میدل ور نیست کافیه همچین کاری کنید:
const setFoo= useFooDispatcher()
React.useEffect(() => {
axios.get("/foo")
.then(res => setFoo(res.data))
}, [ setFoo ])
یا اگر دوست داشتید مستقیم setState کنید مثل کد زیر نیازه یکم تو Provider کدتون رو تغییر بدید:
const setFoo= useFooDispatcher()
React.useEffect(() => {
setFoo(axios.get("/foo"))
}, [ setFoo ])
من داخل سایت جدیدی که زدم هم PersistedState دارم و هم SyncedState که دیتا بین تبهای مرورگر سینک بشه، یعنی اگر سایت من رو داخل ۲ تا تب باز کنید و داخل تب اولی لاگین کنید و یا چیزی به سبد خرید اضافه کنید، همون لحظه داخل تب دوم هم اعمال میشه :)
سرعت لود شدن سایت رو هم حس کنید :)
امتیاز Google Page Insights و GtMetrix هم خودتون چک کنید
نکات پرفورمنسی
- تا جایی که ممکنه از useState برای استیت Provider استفاده نکنید، useReducer به علت اینکه متد dispatch که داره هیچ وقت عوض نمیشه، باعث میشه کامپوننتهای متصل به کانتکست به هیچ وجه اضافه رندر نشن. (از طرفی کد ما با useReducer دقیقا مثل همون ریداکس میشه و خیلی راحت میشه از ریداکس به کانتکست سویچ کرد) اما متد setState که از هوک useState به دست میاد ممکنه عوض بشه ... اطلاعات بیشتر
- مقادیر داخل Provider رو Memorize کنید. اگر درحال ساخت کاستوم هوک بودید و مقدار Dispatcher رو عوض کردید، حتما متد جدید رو Memorize کنید. برای مثال کد زیر رو چک کنید
برای اینکه تو هر رندر فانکشن جدید ساخته نشه و کامپوننتهای متصل رندر اضافی نداشته باشن، با متد useCallback اون متد جدید رو memorize میکنیم
و البته بیشتر مواقع میشه جلوی این قضیه رو با کمی بهتر اندیشیدن گرفت:
استفاده از هوک در کلاس کامپوننت ممکن هستش ؟
بله، با استفاده از HOC میشه از هوکها در کلاس کامپوننتها استفاده کرد.
و هرجا که لازم بود فقط کافیه withCount رو به کامپوننتمون اضافه کنیم:
مثال پیاده سازی شده کد بالا:
آیا کانتکست Devtools داره ؟
بله، شما با React Devtools کانتکست رو هم میتونید دیباگ کنید.
با یک کلیک میتونید تمام دادههای یک کامپوننت (استیت، پراپ، کانتکس و ...) رو داخل کنسول لوگ کنید و یا کامپوننت رو دیباگ کنید و کلی قابلیت فوقالعاده دیگه ...
برای لوگ کردن اطلاعات کامپوننت کافیه اول اون کامپوننت رو انتخاب کنید و از منوی سمت راست صفحه روی آیکن "حشره یا باگ" کلیک کنید (دومین گزینه از راست)
و اطلاعات کامپوننت ما به شکل زیر داخل کنسول چاپ میشن.
ما به این نتیجه رسیدم که کانتکست برای پروژهای که داریم کافی نیست ؟ آیا هنوز باید از ریداکس استفاده کنیم ؟
خیر، ابزاری به نام MOBX وجود داره که هزاران بار از ریداکس ساده تر هستش، شایدم میلیونها بار
معمولا داخل یک وبسایت یا برنامه ریاکت نیتیو به چند کانتکست نیاز داریم ؟
معمولا ۳،۴ تا نیاز هستش (UserProvider، CartProvider، ToastProvider و ModalProvider)
ما با کانتسکت نمیتونیم خارج از React Tree چیزی رو به کانتکست اضافه کنیم یا مقدارشو بخونیم ؟
بله این امکان وجود نداره که مثل ریداکس از dispatch و getState خارج از ریاکت استفاده کنیم. ولی تا وقتی که میشه هرچیزی رو به صورت کامپوننت رندر کرد چرا باید همچین کاری کنیم ؟
مثلا استفاده از Axios و نیاز به تغییر وضعیت کاربر در کانتکست زمانی که درخواست ما با خطای ۴۰۱ مواجه شد!
function AxiosInitilizer({ children }) {
const dispatch = useUserDispatch()
React.useEffect(() => {
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response.status === 401) {
// dispatch something into UserProvider
history.push("/login")
}
return Promise.reject(error)
})
}, [])
return children
}
و بعدش فقط کافیه این کامپوننت رو کنار کامپوننتهای دیگه رندر کنیم
<UserProvider>
<CartProvider>
<ToastProvider>
<AxiosInitilizer>
...
</AxiosInitilizer>
</ToastProvider>
</CartProvider>
</UserProvider>
بعد از این مقاله میرسیم به قسمت سوم و پایانی که چندتا مثال از کانتکست رو تو پروژههای واقعی پیاده سازی میکنم و تمرکز رو روی هوک useReducer میزارم.
با این تفاسیر دوره آموزش هوک تغریبا کامل میشه.
داخل مقاله زیر درباره useState و useEffect و کمی useRef و یکم useContext صحبت کردم:
تو قسمت قبلی و این قسمت سعی کردم useContext رو توضیح بدم.
همچنین در ابتدای همین مقاله الگوریتم Memorize رو توضیح دادم و موارد استفاده از useMemo و useCallback رو تشریح کردم.
شاید در آیندهایی نه چندان دور مقالهایی درباره هوکها با عنوان ناگفتههای هوک نوشتم و هوک useEffect و useRef و useLayoutEffect رو بیشتر توضیح دادم.
سخن پایانی
اول اینکه خسته نباشید تا اینجا این مقاله رو دنبال کردید. امیدوارم که واستون مفید بوده باشه و من دوست دارم نظرات و سوالاتتون رو درباره کانتکست و این مقاله بدونم.
دوم اینکه منو داخل توییتر دنبال کنید معمولا درباره ریاکت و جاوااسکریپت اونجا مینویسم.
سوم اینکه منو داخل ویرگول دنبال کنید که تا هردفعه که مقاله جدید مینویسم برای شما ایمیل بیاد.
و در آخر کانال تلگرام خودم رو دنبال کنید، داخل کانال لینک ویدیوهای آموزشی خودم رو میزام (از دوره React Router 5 تا الان که این مقاله رو مینویسم ۱۲ قسمت ضبط شده)
مطلبی دیگر از این انتشارات
اگر React و Chrome بچه داشتند. (بخش اول، ژن React)
مطلبی دیگر از این انتشارات
از Reactjs تا گوگل
مطلبی دیگر از این انتشارات
فانکشنال کامپوننت در مقابل کلاس کامپوننت