React Hooks iesācējiem - smadzenēm draudzīgs ceļvedis useState and useEffect

"Kas pie velna ir āķi?"

Es atklāju, ka to jautāju tieši tad, kad man likās, ka esmu aptvēris visu React pamatu. Tāda ir frontend izstrādātāja dzīve, spēle vienmēr mainās. Ievadiet Āķi.

Vienmēr ir patīkami uzzināt kaut ko jaunu, vai ne? Protams! Bet dažreiz mums ir jājautā sev: "Kāpēc? Kāda jēga šai jaunajai lietai? Vai man tā ir jāapgūst"?

Ar āķiem atbilde ir "nav uzreiz". Ja jūs esat mācījies React un līdz šim izmantojāt uz klasēm balstītus komponentus, nav jāsteidzas pāriet uz āķiem. Āķi nav obligāti, un tie var darboties kopā ar esošajiem komponentiem. Vai jūs to neienīstat, kad jums ir jāpārraksta visa koda bāze, lai kaut kas jauns darbotos?

Jebkurā gadījumā, šeit ir daži iemesli, kāpēc āķi vispār tika ieviesti un kāpēc iesācējiem iesaku tos iemācīties.

Valsts izmantošana funkcionālajos komponentos

Pirms āķiem mēs nevarējām izmantot stāvokli funkcionālajos komponentos. Tas nozīmē, ka, ja jums ir labi izstrādāts un pārbaudīts funkcionāls komponents, kuram pēkšņi ir jāuzglabā stāvoklis, jums ir jāpieņem sāpīgais uzdevums atjaunot savu funkcionālo komponentu klases komponentā.

Urā! Valsts atļaušana funkcionālajos komponentos nozīmē, ka mums nav jāpārstrādā prezentācijas komponenti. Plašāku informāciju skatiet šajā rakstā.

Klases komponenti ir neveikli

Atzīsim, ka klases komponentiem ir daudz katlu. Konstruktori, iesiešana, visur izmantojot "šo". Izmantojot funkcionālos komponentus, tas tiek novērsts daudz, tāpēc mūsu kodu ir vieglāk ievērot un uzturēt.

Jūs varat uzzināt vairāk par to React dokumentos:

Lasāmāks kods

Tā kā āķi ļauj mums izmantot funkcionālos komponentus, tas nozīmē, ka koda ir mazāk nekā klases komponentos. Tas padara mūsu kodu vieglāk lasāmu. Nu, tā ir ideja.

Mums nav jāuztraucas par mūsu funkciju saistīšanu vai jāatceras, kas arī ir “šis”, utt. Mēs varam uztraukties par tā koda uzrakstīšanu.

Ja jūs tikko sākat darbu ar React, manā emuārā ir daudz iesāktu ziņu, kas varētu jums palīdzēt! Pārbaudiet to šeit:

Reakcijas stāvokļa āķis

Ah, valsts. React ekosistēmas stūrakmens. Saslapināsim kājas ar āķiem, ieviešot visizplatītāko āķi, ar kuru jūs strādāsit - useState().

Apskatīsim klases komponentu, kuram ir stāvoklis.

 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;

Izmantojot React Hooks, mēs varam pārrakstīt šo komponentu un noņemt daudz lietu, padarot to vieglāk saprotamu:

 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;

Pēc sejas ir mazāks kods, bet kas notiek?

Reakcijas stāvokļa sintakse

Tātad mēs esam redzējuši mūsu pirmo āķi! Urā!

 const [count, setCount] = useState(); 

Būtībā masīviem tas izmanto destrukturējošu uzdevumu. useState()Funkcija dod mums 2 lietas:

  • mainīgais, lai saglabātu stāvokļa vērtību , šajā gadījumā to sauc count- funkcija, lai mainītu vērtību , šajā gadījumā to sauc setCount.

Jūs varat nosaukt tos, kā vēlaties:

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

Un jūs varat tos izmantot visā kodā, piemēram, parastos mainīgos / funkcijas:

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

This is a counter using hooks

{count}

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

Ievērojiet useStateāķi augšpusē. Mēs deklarējam / iznīcinām 2 lietas:

  • counter: vērtība, kas saglabās mūsu valsts vērtību
  • setCounter: funkcija, kas mainīs mūsu countermainīgo

Turpinot kodu, jūs redzēsiet šo rindu:

{count}

Šis ir piemērs tam, kā mēs varam izmantot stāvokļa āķa mainīgo. JSX ietvaros mēs ievietojam countmainīgo, {}lai to izpildītu kā JavaScript, un savukārt countvērtība lapā tiek atveidota.

Salīdzinot to ar veco "klases balstīto" stāvokļa mainīgā izmantošanas veidu:

{this.state.count}

Jūs pamanīsit, ka mums vairs nav jāuztraucas par lietošanu this, kas padara mūsu dzīvi daudz vieglāku - piemēram, VS koda redaktors brīdinās mūs, ja {count}tas nav definēts, ļaujot mums jau laikus pieļaut kļūdas. Tā kā {this.state.count}koda palaišana nezina, vai tas nav definēts.

Tālāk uz nākamo rindu!

  setCount(count + 1)}>Click to Increment 

Here, we're using the setCount function (remember we destructured/declared this from the useState() hook) to change the count variable.

When the button is clicked, we update the count variable by 1. Since this is a change of state this triggers a rerender, and React updates the view with the new count value for us. Sweet!

How can I set the initial state?

You can set the initial state by passing an argument to the useState() syntax. This can be a hardcoded value:

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

Or can be taken from the props:

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

This would set the count value to whatever the props.initialValue is.

That sums up useState(). The beauty of it is that you can use state variables/functions like any other variable/function you would write yourself.

How do I handle multiple state variables?

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.

Tagad aizbrauciet uz Reac āķu pasauli un mēģiniet pats uzcelt lietas! Runājot par pašmāju celtniecību ...