React Hooks للمبتدئين - دليل مناسب للعقل حول useState و useEffect

"ما هيك الخطافات؟"

وجدت نفسي أسأل هذا تمامًا كما اعتقدت أنني غطيت كل أسس React. هذه هي حياة مطور الواجهة الأمامية ، تتغير اللعبة دائمًا. أدخل الخطافات.

من الجيد دائمًا أن تتعلم شيئًا جديدًا ، أليس كذلك؟ بالطبع! لكن في بعض الأحيان علينا أن نسأل أنفسنا "لماذا؟ ما الهدف من هذا الشيء الجديد؟ هل يجب أن أتعلمه"؟

مع الخطافات ، الجواب "ليس على الفور". إذا كنت تتعلم React ، وكنت تستخدم مكونات قائمة على الفصل حتى الآن ، فلا داعي للاندفاع للانتقال إلى الخطافات. الخطافات اختيارية ويمكن أن تعمل جنبًا إلى جنب مع المكونات الموجودة لديك. ألا تكره ذلك عندما تضطر إلى إعادة كتابة قاعدة التعليمات البرمجية بالكامل للحصول على شيء جديد يعمل؟

على أي حال ، إليك بعض الأسباب التي أدت إلى تقديم الخطافات في المقام الأول ولماذا أوصي المبتدئين بتعلمها.

استخدام الحالة في المكونات الوظيفية

قبل الخطافات ، لم نتمكن من استخدام الحالة في المكونات الوظيفية. هذا يعني أنه إذا كان لديك مكون وظيفي جيد الصنع ومُختبَر ويحتاج فجأة إلى حالة التخزين ، فأنت عالق في المهمة المؤلمة المتمثلة في إعادة هيكلة المكون الوظيفي الخاص بك إلى مكون فئة.

يا هلا! السماح بالحالة داخل المكونات الوظيفية يعني أنه لا يتعين علينا إعادة تشكيل مكونات العرض التقديمي الخاص بنا ، راجع هذه المقالة لمزيد من المعلومات.

مكونات الطبقة ثقيلة

دعونا نواجه الأمر ، تأتي مكونات الفصل مع الكثير من النماذج المعيارية. المنشئون ، ملزمون ، باستخدام "هذا" في كل مكان. يؤدي استخدام المكونات الوظيفية إلى إزالة الكثير من هذا ، لذلك يصبح من السهل متابعة التعليمات البرمجية الخاصة بنا والحفاظ عليها.

يمكنك قراءة المزيد حول هذا في مستندات React:

كود أكثر قابلية للقراءة

نظرًا لأن الخطافات تتيح لنا استخدام المكونات الوظيفية ، فهذا يعني أن هناك رمزًا أقل مقارنة بمكونات الفئة. هذا يجعل الكود الخاص بنا أكثر قابلية للقراءة. حسنًا ، هذه هي الفكرة على أي حال.

لا داعي للقلق بشأن ربط وظائفنا ، أو تذكر ما يتعلق بـ "هذا" أيضًا ، وما إلى ذلك. يمكننا القلق بشأن كتابة الكود الخاص بنا بدلاً من ذلك.

إذا كنت بدأت للتو في استخدام React ، فلدي مجموعة من منشورات البدء على مدونتي والتي قد تساعدك! تحقق من ذلك هنا:

رد فعل هوك الدولة

آه ، دولة. حجر الزاوية في نظام React البيئي. دعنا نبلل أقدامنا بالخطافات من خلال إدخال الخطاف الأكثر شيوعًا الذي ستعمل به - useState().

دعنا نلقي نظرة على مكون فئة له حالة.

 import React, { Component } from 'react'; import './styles.css'; class Counter extends Component { state = { count: this.props.initialValue, }; setCount = () => { this.setState({ count: this.state.count + 1 }); }; render() { return ( 

This is a counter using a class

{this.state.count}

Click to Increment ); } } export default Counter;

باستخدام React Hooks ، يمكننا إعادة كتابة هذا المكون وإزالة الكثير من العناصر ، مما يسهل فهمه:

 import React, { useState } from 'react'; function CounterWithHooks(props) { const [count, setCount] = useState(props.initialValue); return ( 

This is a counter using hooks

{count}

setCount(count + 1)}>Click to Increment ); } export default CounterWithHooks;

في ظاهر الأمر يوجد كود أقل ، لكن ما الذي يحدث؟

رد فعل الحالة

لذلك رأينا أول خطاف لنا! يا هلا!

 const [count, setCount] = useState(); 

يستخدم هذا في الأساس تخصيص التدمير للمصفوفات. و useState()ظيفة يعطينا 2 أشياء:

  • متغير للاحتفاظ بقيمة الحالة ، في هذه الحالة ، يطلق عليه count- وظيفة لتغيير القيمة ، في هذه الحالة ، يطلق عليه setCount.

يمكنك تسمية هذه ما تريد:

 const [myCount, setCount] = useState(0); 

ويمكنك استخدامها في جميع أنحاء الكود مثل المتغيرات / الوظائف العادية:

 function CounterWithHooks() { const [count, setCount] = useState(); return ( 

This is a counter using hooks

{count}

setCount(count + 1)}>Click to Increment ); }

لاحظ useStateالخطاف في الأعلى. نحن نعلن / ندمر شيئين:

  • counter: قيمة تحمل قيمة دولتنا
  • setCounter: وظيفة ستغير counterمتغيرنا

بينما نواصل عبر الكود ، سترى هذا السطر:

{count}

هذا مثال على كيفية استخدام متغير ربط الحالة. داخل JSX الخاص بنا ، نضع متغيرنا في countالداخل {}لتنفيذه كـ JavaScript ، وبالتالي يتم عرض countالقيمة على الصفحة.

مقارنة هذا بالطريقة القديمة "القائمة على الطبقة" لاستخدام متغير الحالة:

{this.state.count}

ستلاحظ أننا لم نعد بحاجة إلى القلق بشأن thisالاستخدام ، مما يجعل حياتنا أسهل كثيرًا - على سبيل المثال ، سيقدم لنا محرر VS Code تحذيرًا إذا {count}لم يتم تحديده ، مما يتيح لنا اكتشاف الأخطاء مبكرًا. في حين أنه لن يعرف ما إذا كان {this.state.count}غير محدد حتى يتم تشغيل الكود.

إلى السطر التالي!

  setCount(count + 1)}>Click to Increment 

هنا ، نحن نستخدم setCountالوظيفة (تذكر أننا دمرنا / أعلنا هذا من useState()الخطاف) لتغيير countالمتغير.

عندما يتم النقر فوق الزر ، نقوم بتحديث countالمتغير بواسطة 1. نظرًا لأن هذا يعد تغييرًا في الحالة ، يؤدي ذلك إلى تشغيل إعادة الإرسال ، وتقوم React بتحديث العرض countبالقيمة الجديدة بالنسبة لنا. حلو!

كيف يمكنني تحديد الحالة الأولية؟

يمكنك ضبط الحالة الأولية عن طريق تمرير وسيطة إلى useState()بناء الجملة. يمكن أن تكون هذه قيمة مضمنة:

 const [count, setCount] = useState(0); 

أو يمكن أن تؤخذ من الدعائم:

 const [count, setCount] = useState(props.initialValue); 

هذا من شأنه أن يحدد countالقيمة مهما كان props.initialValue.

هذا يلخص useState(). الجميل في ذلك أنه يمكنك استخدام متغيرات / وظائف الحالة مثل أي متغير / وظيفة أخرى تكتبها بنفسك.

كيف أتعامل مع متغيرات الحالة المتعددة؟

This is another cool thing about hooks. We can have as many as we like in a component:

 const [count, setCount] = useState(props.initialValue); const [title, setTitle] = useState("This is my title"); const [age, setAge] = useState(25); 

As you can see, we have 3 seperate state objects. If we wanted to update the age for example, we just call the setAge() function. The same with count and title. We no longer are tied to the old clunky class component way where we have one massive state object stored using setState():

 this.setState({ count: props.initialValue, title: "This is my title", age: 25 }) 

So, what about updating things when props or state changes?

When using hooks and functional components, we no longer have access to React lifecycle methods like componentDidMount, componentDidUpdate, and so on. Oh, dear! Do not panic my friend, React has given us another hook we can use:

  • Drum Roll *

Enter useEffect!

The Effect hook (useEffect()) is where we put "side effects".

Eh, side effects? What? Let's go off-track for a minute and discuss what a side effect actually is. This will help us understand what useEffect() does, and why it's useful.

A boring computer-y explanation would be.

"In programming, a side effect is when a procedure changes a variable from outside its scope"

In React-y terms, this means "when a component's variables or state changes based on some outside thing". For example, this could be:

  • When a component receives new props that change its state
  • When a component makes an API call and does something with the response (e.g, changes the state)

So why is it called a side effect? Well, we cannot be sure what the result of the action will be. We can never be 100% certain what props we are going to receive, or what the response from an API call would be. And, we cannot be sure how this will affect our component.

Sure we can write code to validate, and handle errors, and so on, but ultimately we cannot be sure what the side effects of said things are.

So for example, when we change state, based on some outside thing this is know as a side effect.

With that out of the way, let's get back to React and the useEffect Hook!

When using functional components we no longer have access to life cycle methods like componentDidMount(), componentDidUpdate() etc. So, in effect (pun intended), the useEffect hooks replace the current React Life Cycle hooks.

Let's compare a class-based component with how we use the useEffect hook:

import React, { Component } from 'react'; class App extends Component { componentDidMount() { console.log('I have just mounted!'); } render() { return Insert JSX here ; } } 

And now using useEffect():

function App() { useEffect(() => { console.log('I have just mounted!'); }); return Insert JSX here ; } 

Before we continue, it's important to know that, by default, the useEffect hook runs on every render and re-render. So whenever the state changes in your component or your component receives new props, it will rerender and cause the useEffect hook to run again.

Running an effect once (componentDidMount)

So, if hooks run every time a component renders, how do we ensure a hook only runs once when the component mounts? For example, if a component fetches data from an API, we don't want this happening every time the component re-renders!

The useEffect() hook takes a second parameter, an array, containing the list of things that will cause the useEffect hook to run. When changed, it will trigger the effect hook. The key to running an effect once is to pass in an empty array:

useEffect(() => { console.log('This only runs once'); }, []); 

So this means the useEffect hook will run on the first render as normal. However, when your component rerenders, the useEffect will think "well, I've already run, there's nothing in the array, so I won't have to run again. Back to sleep for me!" and simply does nothing.

In summary, empty array = useEffect hook runs once on mount

Using effects when things change (componentDidUpdate)

We've covered how to make sure a useEffect hook only runs once, but what about when our component receives a new prop? Or we want to run some code when the state changes? Hooks let us do this as well!

 useEffect(() => { console.log("The name props has changed!") }, [props.name]); 

Notice how we are passing stuff to the useEffect array this time, namely props.name.

In this scenario, the useEffect hook will run on the first load as always. Whenever your component receives a new name prop from its parent, the useEffect hook will be triggered, and the code within it will run.

We can do the same thing with state variables:

const [name, setName] = useState("Chris"); useEffect(() => { console.log("The name state variable has changed!"); }, [name]); 

Whenever the name variable changes, the component rerenders and the useEffect hook will run and output the message. Since this is an array, we can add multiple things to it:

const [name, setName] = useState("Chris"); useEffect(() => { console.log("Something has changed!"); }, [name, props.name]); 

This time, when the name state variable changes, or the name prop changes, the useEffect hook will run and display the console message.

Can we use componentWillUnmount()?

To run a hook as the component is about to unmount, we just have to return a function from the useEffect hook:

useEffect(() => { console.log('running effect'); return () => { console.log('unmounting'); }; }); 

Can I use different hooks together?

Yes! You can use as many hooks as you want in a component, and mix and match as you like:

function App = () => { const [name, setName] = useState(); const [age, setAge] = useState(); useEffect(()=>{ console.log("component has changed"); }, [name, age]) return( Some jsx here... ) } 

Conclusion - What Next?

There you have it. Hooks allow us to use good old fashioned JavaScript functions to create simplier React components, and reduce alot of boilerplate code.

الآن ، انطلق في عالم خطافات Reac وحاول بناء الأشياء بنفسك! بالحديث عن بناء الأشياء بنفسك ...