شرح مكتبة Recoil.js الجديدة لادارة الـ State من Facebook

لاعب جديد يدخل على الساحة بطريقة لعب اجمع جميع الخبراء على انها اسهل ما وجد برعاية من العملاق facebook

نعم انها مكتبة Recoiljs مكتبة لادارة الحالة ببساطة تتبنى مفهوم react لانها من تطوير فريقها ، نعم قالو انها ليست رسمية لكنها تحت غطائهم :heart_eyes:

هيا مصممة خصيصا للـ react ليس مثلها مثل Redux و mobx ليس عليك تعلم عديد المبادئ ;و المصطلحات وطرق العمل المعقدة ومحاولة فهم اشياء خارج مفهوم react

قبل ان نبدا افترض انك تعاملت مع React hooks :v:

تعال نتفق على ان نتفق على انه معضم مكتبات ادارة الـ state تحاول جعلها global بحيث يمكن الوصول اليها من اي component و هذا الامر ليس مقتصر على React فقط بل معضم الاطر في الساحة

لابد وانك استعملت useState لتعيين state value للـ component الخاص بك وكما يعلمون ونعلم انه يمكنك الوصول لتلك value من اي مكان في الـ component :smile:

حسنا ما رايك ان اخبرك ان Recoil هو نفس useState لكن بامكانية انك يمكنك الوصول لل value و التعديل عليها من اي مكان في تطبيقك :grinning:

نعم الامر بهذه البساطة حبيبي في الله تاتي مكتبة Recoil بمفهومين اساسين لا غير هما atoms و selectors


ولنفهم هذه المفاهيم سنقوم ببناء تطبيقنا الاول بها

اولا لابد من انشاء مشروع react جديد

و الان لنقم باضافة Recoil.js لمشروعنا

عبر npm

npm install recoil

او عبر Yarn

yarn add recoil

و الان لنقم بافراغ ملف App.js ونجعله بهذا الشكل

import React from 'react';
import "./App.css"


const App = () => {
  return (
    <div className="App" >
      <h1> Coretabes </h1>
    </div>
  );
}

export default App;

و الان دخول الاعب الجديد للميدان :sweat_smile:
اول شيء نقوم به هو استدعاء الـ RecoilRoot ونقوم باستخدامه بهذا الشكل

import React from 'react';
import "./App.css"

// New
import { RecoilRoot } from 'recoil';

const App = () => {
  return (
    <RecoilRoot> 
      <div className="App" >
        <h1> Coretabes </h1>
      </div>
    </RecoilRoot>
  );
}

export default App;

ولتوضح ما قمنا به ، قمنا باستدعاء component الـ RecoilRoot وهو المكون الذي سنغلف يه اعلى مكون لدينا الذي سيتيح لنا الوصول للـ state من اي مكان في تطبيقنا

والان لنقم بانشاء component الذي هو عبارة عن عداد بسيط اسمه Counter ولنقم باضافته في App.js

import React from 'react';
import "./App.css"


import { RecoilRoot } from 'recoil';

// New
const Counter = () => {
  return <button>0</button>
}

const App = () => {
  return (
    <RecoilRoot> 
      <div className="App" >
        <Counter/> 
      </div>
    </RecoilRoot>
  );
}

export default App;

و الان لنقم بانشاء اول atom لنا وثم سنشرحه

import React from 'react';
import "./App.css"

// New
import { RecoilRoot, atom } from 'recoil';

// New
const numStateValue = atom({
  key: 'numStateValue',
  default : 0
})

const Counter = () => {
  return <button>0</button>
}

const App = () => {
  return (
    <RecoilRoot> 
      <div className="App" >
        <Counter/> 
      </div>
    </RecoilRoot>
  );
}

export default App;

وللتركيز على الامر هذه هيا طريقة كتابة atom

// (recoil) من (atom) نقوم باستراد دالة 
import { RecoilRoot, atom } from 'recoil';

// (object) التي تستقبل (atom) متغير الحالة يحتوى على دالة 
const numStateValue = atom({
  key: 'numStateValue',
  default : 0
})

ومنه atom هيا دالة تنشئ لنا state value تستقبل object يجب ان يحتوي اول قيمة key المهم ان تكون القيمة قريدة unique لانها تساعد في تتبع الاخطاء و القيمة default وهي القيمة الافتراضية

وهكذا يصبح لدنا state value ( numStateValue ) يمكننا مشاركتها لاي components وهيا قابلة للتحديث وعند تحديثها تتغير في جميع الـ components التي قمنا بمشاركتها فيها و الان لنقم بمشاركتها في اول component لنا وهو Counter

// (useٍState) وهي ستعوض (useRecoilState) نقوم باستيراد 
import { RecoilRoot, atom, useRecoilState } from 'recoil';

// (useRecoilState) باستخدام (state variable) ونقوم بانشاء   
const Counter = () => {
  const [number, setNumber] = useRecoilState(numStateValue)
  return (
    <button onClick={() => setNumber(number + 1)}>
      {number}
    </button>)
}

كما تلاحظ قنا بمشاركة atom الخاص بنا numStateValue الى useRecoilState كقيمة للstate الخاص بالـ component counter وقمنا ايضا انشاء حدث وهو عند النقر على button يقوم بزيادة 1 للقيمة السابقة .

وهكذا يمكنك مشاركة numStatevalue لاي مكون في تطبيقك باستعمال useRecoilState للقرائة و التعديل بكل بساطة

ولكن ماذا لو اردنا فقط عرض قيمة numStatevalue وفي مكون اخر يعني انه سننشئ مكون اخر يقوم بعرض لنا قيمة العداد الخاص بنا

...
...
...

// New
const DisplayCounter = () => {
  const [number, setNumber] = useRecoilState(numStateValue)
  return <h1>{number}</h1>
}

const App = () => {
  return (
    <RecoilRoot>
      <div className="App" >
        <Counter />
        <DisplayCounter />
      </div>
    </RecoilRoot>
  );
}

export default App;

لقد قمنا بانشاء component جديد DisplayCounter و قمنا باضافته للـ App
وقمنا بتعيين state variable باستخدام useRecoilState وقمنا بتعيين قيمتها بـ numStateValue
لنحصل على هذه النتيجة بمجرد تحديث قيمة العداد تتحدث قيمة DisplayCounter

Screen record from 2020-07-03 03.55.34

كما ترى قمنا بمشاركة atom (numStateValue) الخاص بنا بين مكوانتنا لكن ان ركزنا في في الكود فهو يعمل بشكل عادي لكنه ليس عملي
كيف ليس عملي ؟
في الـ component الاول Counter كنا نحتاج للتعديل وقرائة قيمة الـ atom الخاص بنا
و في الـcomponent الثاني DisplayCounter لم نكن نحتاج للتعديل بل احتجنا فقط لقيمة state

اذا الحل بسيط نقوم باستعمال خطاف useRecoilValue نقوم باستيراده من recoil ببساطة يسمح لنا useRecoilValue بقرائة قيمة state value فقط هكذا

import { RecoilRoot, atom, useRecoilState, useRecoilValue } from 'recoil';

const DisplayCounter = () => {
  // const [number, setNumber] = useRecoilState(numStateValue)
  const number = useRecoilValue(numStateValue)
  return <h1>{number}</h1>
}

وهكذا اصبح كودنا نضيف وعملي اكثر وحصلنا على نفس النتيجة وهذا الكود الخاص بنا كاملا

import React from 'react';
import "./App.css"

import { RecoilRoot, atom, useRecoilState, useRecoilValue } from 'recoil';

const numStateValue = atom({
  key: 'numStateValue',
  default: 0
})

const Counter = () => {
  const [number, setNumber] = useRecoilState(numStateValue)
  return (<button onClick={() => setNumber(number + 1)}>{number}</button>)
}

const DisplayCounter = () => {
  const number = useRecoilValue(numStateValue)
  return <h1>{number}</h1>
}

const App = () => {
  return (
    <RecoilRoot>
      <div className="App" >
        <Counter />
        <DisplayCounter />
      </div>
    </RecoilRoot>
  );
}

export default App;

وهكذا نكون تعرفنا على atoms وطريقة مشاكرتها بين components نظريا وتطبيقيا

و الان لنتعرف المفهوم الثاني معنا في هذه المكتبة نعم انها Selectors

لو اردنا انشاء قيمة تعتمد على state value السابقة و قابلة للتحديث بمجرد تحديث الـ state value بمفهوم اخر تستند في قيمتها على state value

هنا ياتي دور selectors ولنفهمها اكثر سنقوم بالتعامل معها في تطبيقنا البسيط
نريد ان يتم ضرب القيمة الحالية لل atom numStateValue في 2 وكل مرة تتغير القيمة يقوم selector بتحديث نفسه
ال selector يتعامل مع atom او selectors اخر و الان ناتي للتطبيق

import { RecoilRoot, atom, useRecoilState, useRecoilValue, selector } from 'recoil';

const multiplicationNumber = selector({
  key: 'multiplicationNumber',
  get: ({get}) => {
    return get(numStateValue) * 2;
  } 
})

الـ selector يستقبل object يحتوي على key ويجب ان يكون فريد unique لا يكون مستخدم من قبل اي selector or atom و القيمة الثانية هيا get و تكون دالة تستقبل خاصية get تقوم بجلب اي selector او atom كما تلاحظ قمنا بجلب atom الخاص بنا numStateValue وقمنا بضربه في 2 و الان بقي امر واحد لنقوم به وهو عرض نتيجة هذا selector

const DisplayCounter = () => {
  const number = useRecoilValue(numStateValue)
  // New
  const multiplication = useRecoilValue(multiplicationNumber)
  return (
    <div>    
      <h1>{number}</h1>
      <h1>Multiplication Number : {multiplication}</h1>  
    </div>
    )
}

لقد قمنا بالتعديل على الـ component DisplayCounter وقمنا بعرض قيمة الـ selector multiplicationNumber بكل بساطة باستخدام useRecoilValue التي قلنا انها للقرائة فقط

ويمكنك مشاركة الـ selector في اي components في تطبيقك مثله مثل atom
وستكون هذه هيا النتيجة النهائية لتطبيقنا

وهذا الكود النهائي لتطبيقنا

import React from 'react';
import "./App.css"

import { RecoilRoot, atom, useRecoilState, useRecoilValue, selector } from 'recoil';

const numStateValue = atom({
  key: 'numStateValue',
  default: 0
})

const multiplicationNumber = selector({
  key: 'multiplicationNumber',
  get: ({get}) => {
    return get(numStateValue) * 2;
  } 
})

const Counter = () => {
  const [number, setNumber] = useRecoilState(numStateValue)
  return (<button onClick={() => setNumber(number + 1)}>{number}</button>)
}

const DisplayCounter = () => {
  const number = useRecoilValue(numStateValue)
  const multiplication = useRecoilValue(multiplicationNumber)
  return (
    <div>    
      <h1>{number}</h1>
      <h1>Multiplication Number : {multiplication}</h1>  
    </div>
    )
}

const App = () => {
  return (
    <RecoilRoot>
      <div className="App" >
        <Counter />
        <DisplayCounter />
      </div>
    </RecoilRoot>
  );
}

export default App;

اذا اردت تجربته لايف و التعديل عليه هذا الرابط
اضـغـط هـــــــــنا

الامر بسيط فقط افتح محررك :upside_down_face:
استودعكم الله :innocent:

المصادر :



5 Likes

لم اصل للمرحلة التى يجب أن احكم عليها ولكن مجال الويب سريع التطور شكراً على الموضوع :smile:

1 Like

ولهذا نحاول ان نجعل المحتوى العربي يسير مع هذا التطور :v:
لايستطيع احد الحكم عليها لانها في بداياتها لكنها من الحلول التي تبشر بالخير :grin:
العفو اخي @abdallahmiri

1 Like

موضوع جد قيم شكرا لك على شرحك لهذه التقنية

1 Like

شرح في قمة الروعة بدون مبالغة الحقيقة مشاء الله :heart_eyes:

عندي سؤال يا حسين هل هاته الطريقة كافية لبناء تطبيق متوسط الحجم؟ أم هي فقط لمشروع صغير؟

2 Likes

العفو اخي مصعب :black_heart:

ربي يخليك اخي هشام :black_heart:

صراحة لم اجربها في اي مشروع الا لغرض فهمها ولكن قرات عنها الكثير
و الكثير يفضلها على Context API لانك تحتاج مزود فقط اما Context API فانت تحتاج مزود ومستهلك و الكثير من الاستيراد

اما عن Redux فـ recoil لا تقدم حاليا ما يقدمه ولكن الجميع يعقد عليها امال كبيرة لانها خالفت جميع المفاهيم السابقة وجعلتها اكثر بساطة وهي قضية وقت فقط حتى تصبح الخيار الاول لادارة state

وكان هذا اجمل ما قراته عنها

ان Recoiljs يفكر مثل React :black_heart:

3 Likes

وصلت الفكرة :ok_hand: شكرا مجددا حسين

2 Likes

شكرا حسين على الموضوع المتميز :+1:

بما ان اللاعب الجديد برعاية ودعم فيسبوك فاعتقد انه لن يمر وقت طويل حتى يصبح ضمن تشكيلة رياكت الاساسية :smile: ويتم دمجه بشكل كامل.

2 Likes

كما يقولون ولد وفي فمه ملعقة من ذهب :joy: :joy:

1 Like

:joy: :joy:

شرح رائع كالعادة يا حسين وموضوع متميز يتكلم عن تقنيات حديثة رائعة :ok_hand:

1 Like

ربي يخليك اخي ياسر :black_heart:

1 Like