یه برنامه نویس معمولی لینوکس کار
نوشتن HOC در React به صورت Functional Component مثلا connect توی redux
تایتل خیلی طولانی شد . نه؟
چون مفهوم HOC هنوز به عنوان یک عبارت قابل جستجو ، جا نیفتاده .
میخوام ببینیم که چجوری میشه یک فانکشن نوشت شبیه به connect توی redux که زمان اکسپورت کردن کامپوننت ، داخل اون تابع این اتفاق بیفته .
تمام عبارتی که بالا گفتم رو بهش میگن HOC یا High-order-Component .
همونطور که میدونیم ، جاوا اسکریپت از بهترین زبان های Functional base موجوده . توابع نمیتونن مثل اشیاء ، اولویت داشته باشن . برای مثال شما میتونید داخل جاوا ، اولیت یک کلاس رو به یک کلاس دیگه با استفاده از extends و implement مشخص کنین . مثلا کلاس hello از کلاس HelloBase ارث میبره پس HelloBase اولویت بالاتری داره و از توابع میشه استفاده کرد .
خوب این مثال داخل جاوا اسکریپت از صفر شدنی نیست . مگر اینکه از es6 استفاده کنیم و مفهوم class که یک مفهوم "Syntactical Sugar " هست .
Classes are in fact "special functions", and just as you can define function expressions and function declarations, the class syntax has two components: class expressions and class declarations.
خوب برای اینکه بتونیم این مفاهیم رو داشته باشیم ، از high-order-functions استفاده میکنیم و میتونیم مفاهیم ارث بری رو پیاده کنیم .
داخل ری اکت به علت component base بودن ، مفهوم high-order-component یا HOC داریم .
من یک مدل خیلی راحت رو پیاده سازی کردم که از سمپل cra هست .
خوب فرض کنید که من یک کامپوننت دارم و میخوام تا زمانی که props ها موجود نیستند ، به من Loading... نمایش بده و وقتی props اومد ، محتویات کامپوننت . چیز پیچیده ای نیست . اما مین میخوام که Loading رو به شکل یک HOC بنویسم .
اول از همه App.js رو به شکل زیر مینویسم .
import React, {Component} from 'react';
import './App.css';
import Head from "./Head";
const App = () => {
return (
<div className="App">
<Head hello={"Do it Now"}/>
</div>
);
}
export default App;
حالا یک نگاه میندازم به Head . کامپوننت Head چیز خاصی نیست .
تا الان اصلا کار پیچیده ای نکردم .
import React from "react";
import logo from "./logo.svg";
const Header=(props)=>{
return (
<header className="App-header">
{props.hello}
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
)
}
export default Header;
خوب الان که پروژه اجرا بشه همه چیز سر جاشه .
اما من میخوام یک ثانیه بهم Loading رو نمایش بده و بعدش کاپوننت اجرا بشه .
خوب میام داخل پوشه HOC یک فایل به اسم Loading میسازم .
import React,{useState,useEffect} from "react"
export const Loading = WrappedComponent => {
return (props) => {
const [hello,setHello]=useState("");
useEffect(()=>{
setTimeout(()=>{
setHello(props.hello)
},1000)
})
return(hello ?
<WrappedComponent {...props}/>
:
<div>Loading...</div>)
}
}
داخل ایت فایل ( که خیلی شبیه به یک کامپوننت نوشته شده ) ابتدا ایمپورت ها رو آوردم و یک state رو اماده کردم که کار کنه . باید بگم اگر hello مقدار داشت ، WrappedComponent رو نمایش بده .
WrappedComponent در واقع میشه کامپوننتی که داخل HOC ما قرار هست که بیاد .
همونطور که بالاتر گفتم HOC یک کامپوننت رو به عنوان آرگومان میگیره و روی اون تصمیم میگیره و کار رو جلو میبره . برای همینه که تابع connect داخل ریداکس ، مهمترین تابع هست . چون تمام state ها رو اونجا هندل میکنه و بالاترین سطح رو داره . یا تابع withStyle داخل کامپوننت material-ui .
برای اینکه یک ثانیه من درست بشه و فکر کنم دارم async کار میکنم ، اومدم داخل useEffect یک setTimeout قرار دادم و گفتم بعد از یک ثانیه state رو تغییر بده و hello رو پر کن . با چی؟
با props ه داخل کامپوننت تعریف شده . خوب همه چیز ظاهرا خوبه . اما چجوری استفده کنیم؟
یک تغییر کوچیک داخل Head.js باید بدیم . به شکل زیر :
import React from "react";
import logo from "./logo.svg";
import {Loading} from "./HOC/Loading";
const Header=(props)=>{
return (
<header className="App-header">
{props.hello}
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
)
}
export default Loading(Header);
Loading رو ایمپورت کردم و انتهای کامپوننت Header رو wrapped کردم داخل Loading .
حالا اجرا کنین و میبینین که یک ثانیه ، Loading... رو میبینید رو بعدش کامپوننت رو .
این کار فوایدش خیلی زیاده . برای مثال شما با context api میخواین تمام استیت ها رو منیج کنین و روی هر تغییر تصمیمگیری کنین ( مثل سبد خرید فروشگاه ) . میتونید یک HOC به اسم shop بنویسید و تمام . همه چیز راحت قابل هندل کردنه .
مطلبی دیگر از این انتشارات
کانفیگ Redux و Redux-Saga در Next.js
مطلبی دیگر از این انتشارات
نگاهی به نسخه جدید ریاکت ۱۶.۶ و نسخه آینده آن
مطلبی دیگر از این انتشارات
استفاده از فونت دلخواه در React Native