Sajjad Ayoubi Personal Page

الگوی تطابقی با استفاده از daggy

October 19, 2018

** بروز رسانی **

جهت تکمیل این بخش ویدیویی درست کردم که میتونید از لینک های زیر مشاهده کنید یوتیوب: https://www.youtube.com/watch?v=3RjCiSnSN0s

آپارات: https://www.aparat.com/v/tncIU

توی این پست قراره در مورد استفاده از pattern match و کتابخونه daggy رو یاد بگیریم و چطور میشه توی پروژه‌ هایی مثل react و react-native از اون استفاده کرد.

مشکل if ها

این تیکه کد خب همه ما قبلا باهاش مواجه شدیم:

render() { if (this.state.isLoading) { return … }

if (this.state.isError) { return … }

if (this.state.isListEmpty) { return … }

return this.state.list.map(item => …) }

این همه دستور تو یه رندر اصلا جالب نیست.

راه حل: pattern matching

توی جاوا اسکریپ بصورت استاندارد چیزی تعریف نشده و خب ما مجبور هستیم از کتابخونه هایی مثل daggy استفاده کنیم.

نمونه کد استفاده از daggy

const Item = daggy.tagged(‘Item’, [‘title’])

const List = daggy.taggedSum(‘List’, { Empty: [], Items: [Item], })

const list = List.Empty

list.cata({ Empty: () => console.log(‘empty…’), Items: items => items.map(item => console.log(item.title)), })

با این روش ما چند تا چیز جدید رو بدست آوردیم:

  1. کدی کم باگ تری داریم
  2. استفاده مجدد ساده تر از کدها
  3. خوانایی راحت تر کد و ساده کردن زندگی دیگران

روش استفاده در مثالی از دنیای واقعی

خب فرض کنیم که شما پروژه خودتون رو با با دستور create-react-app ساختید و کتابخونه daggy رو با دستور yarn add daggy نصب کردید.

src index.js App.js App.css types.js

خب قرار ما یک لیستی رو از api دریافت کنیم و خب باید این لیست از دیتا رو با type تعریف کنیم.

فرض کنید که لیستی که از api ارسال میشه این شکلیه:

const LIST = [ { title: ‘Butter’ }, { title: ‘Bread’ }, { title: ‘Eggs’ }, { title: ‘Fish’ }, { title: ‘Cake :3’ }, ]

const petFetch = () => Promise .resolve(LIST) .then(list => ({ list }))

برای تعریف type ما باید اول بخش بندی کنیم لیستمون رو.

مثلا آبجکت اول این آرایه رو یک Item در نظر میگیرم و بعد مجموعه همه این item ها رو یک page در نظر میگیریم.

const Item = daggy.tagged(‘Item’, [‘title’])

const List = daggy.taggedSum(‘Page’, { Empty: [], Initial: [], Items: [Item], NotFound: [‘searchMessage’], FetchError: [], })

نکته بعد این هست که توی taggedSum ما باید حالت های مختلف type رو تعریف کنیم. مثلا اگه حالت اولیه بود و هنوز دستوری ارسال نشده، یاموقعی که خروجی از api داشتیم و غیره رو تعریف میکنیم.

مرحله بعد استفاده از این تایپ های تعریف شده است

class App extends Component { state = { list: List.Initial, searchString: ”, }

render() { return (

    {this.state.list.cata({ Empty: () =>
  • This list is empty =(
  • , Initial: () =>
  • Loading…
  • , Items: items => items.map(({ title }) =>
  • {title}
  • ), NotFound: seacrhMessage =>
  • There is nothing on your request: ’{seacrhMessage}’
  • , FetchError: () =>
  • Oooooops…
  • , })}
); } }

نیاز به توضیح نداره که کد ما چقدر قشنگ تر شده و از شر اون همه شرط راحت شدیم.

حالا وقته شبیه سازی اینه که ما مثلا دستور رو به api ارسال کردیم و منتظر نتیجه ای هستیم

componentWillMount() { setTimeout(this.fetchList, 2000) }

fetchList = () => petFetch() .then(res => this.wrapList(res.list)) .catch(() => this.setState({ list: List.FetchError }))

wrapList = (list) => { const wrapperList = list.length === 0 ? List.Empty : List.Items(list)

this.setState({ list: wrapperList })

}

حالا اگه از redux استفاده میکنید کافیه این رو توی ریداکستون قرار بدید.

کدهای این پست رو توی گیتهاب میتونید مشاهده کنید.