React Hooks: useState. Rules and tips for component state manipulation.
Tags: react, webdev, javascript
Published: 2020-04-14 11:52:29

* figure out the way useState works;
* logical conditions in render;
* ways to perform an action after changing the state;
* use prevState in a function to change the state;

In this article we’ll figure out what the useState is and the rules to follow for its successful use.

##__How the useSate works?__##

<code>useState</code> is a React hook for managing components rendering. The hook can be used inside each component which needs to be updated and re-rendered according to its state changes.

<code>useState</code> hook is a method that accepts just one argument which is an __initial state__.

This argument could be:
* A function that returns initial state (using for a more complex way of its defining).
* Some initial state.

<code>useState</code> returns an array of two elements:
* Current state.
* Function for updating current state.

Using <code>useState</code> we're able to divide logic into several states and each of it will be applied for proper element inside the render

Keep in mind that hooks have some particular rules of their use:

* Hooks are available for calling only inside the body of a function component. Once you would try to call it inside another function, condition or loop inside the component, you’ll definitely get an error. To avoid getting an invalid hook call, __useState should be initialized at the top of your component__. Moreover, this makes it convenient for the use.
* State value must be updated with given method from useState hook __but not by direct reassignment__ (mutating).

__WRONG:__ <code>ourState = newValue</code>.
__RIGHT:__ <code>changeStateValue(newValue)</code>.

Let’s take a look at the basic use of state when performing conditional rendering:

```jsx
import React from 'react'

  const StateExample = () => {
    const [isElementVisible, changeElementVisibility] = React.useState(true)
  
    const handleHideElement = () => {
      changeElementVisibility(false)
    }
  
    return (
      <div>
        {isElementVisible && <div>Element that should be hidden on button click</div>}
  
        <button onClick={handleHideElement}>Hilde element above</button>
      </div>
    )
  }
  
  export default StateExample
```
Let's see what we’ve done: when doing destructuring assignment, the <code>isElementVisible</code> constant was assigned the initial state, and the <code>changeElementVisibility</code> constant got a function for updating the state.
>How constants and functions should be properly named? The right name should briefly describe the purpose and the role that the constant or function will play in a particular component. It might seem too obvious at the first sight. But this recommendation is often neglected by the developers.

>First things first, by doing this you ensure a rapid navigation across your entire project. In case the whole team is engaged for the project, the given name defines how quickly other developers can navigate through your code and continue working with it. Think of other people and be one step ahead. To get the right idea, try to always visualize yourself being completely new to this project or working in the pair.

To update component state it is vital to know that:

You __should avoid__ to use anonymous function inside <code>onClick</code> and make changes directly out of there. The anonymous function will be initialized anew each time your page will render. Its affect the entire application performance.

You __should use__ a predefined function to describe the state change, in our case <code>handleHideElement</code>. For example, if an element would not be hidden only by clicking on one button, but possibly somewhere else - we can easily reuse the <code>handleHideElement</code> function, reducing the amount of code and improving readability.

In our example, by clicking on the button, <code>handleHideElement</code> is called and the value is passed to <code>changeElementVisibility</code>. Thus, the state changes and the component is being re-rendered, hiding therefore the element we need.
```jsx
import React from 'react'

  const StateExample = () => {
    const [isElementVisible, changeElementVisibility] = React.useState(true)
    
    // Correct usage
    const handleHideElement = () => {
      changeElementVisibility(false)
    }
  
    return (
      <div>
        {isElementVisible && <div>Element that should be hidden on button click</div>}
        {/* Don't do that */}
        <button onClick={() => changeElementVisibility(false)}>Hilde element above</button>
        {/* Correct usage */}
        <button onClick={handleHideElement}>Hilde element above</button>
      </div>
    )
  }
  
  export default StateExample
```
By the way, when performing conditional rendering, it is correct to use a construction with a logical <code>AND(&&)</code>. If the left side of the condition (<code>isElementVisible</code>) returns <code>false</code>, React will ignore this element in the render. In case the left side returns <code>true</code> - the construct returns the element that React will draw in the browser.
##__What if you want to perform an action after changing state?__##
Working with classes you’re able to pass a callback function as the second argument to the <code>setState</code> method and this <code>callback</code> function would be fired as soon as state has been changed. However, using the <code>useState</code> hook we’re not able to do that. To reach the goal, you should apply the <code>useEffectmethod</code> by adding the dependency <code>isElementVisible</code> as the second argument. Thus, the function will be called each time the component state changes.
>Be aware that <code>useEffect</code> method will be called at the very first component rendering and make sure you take this into account when building logic.
```jsx

  import React from 'react'

  const StateExample = () => {
    const [isElementVisible, changeElementVisibility] = React.useState(true)
  
    React.useEffect(() => {
      // function will be called on each "isElementVisible" change
    }, [isElementVisible])
  
    const handleHideElement = () => {
      changeElementVisibility(false)
    }
  
    return (
      <div>
        {isElementVisible && <div>Element that should be hidden on button click</div>}
  
        <button onClick={handleHideElement}>Hilde element above</button>
      </div>
    )
  }
  
  export default StateExample
  
```
##__Using prevState in a function to change state__##
As you can see in the example above, the function that updates the state takes an updated value as an argument. Yet this is not all. It can also take a function that returns an updated value. The argument of this function is the current state until the next update takes place.

The example below shows the function used as an argument to update the state of input fields.
```jsx

  import React from 'react'

  // local variables
  const FIELD_NAMES = {
    FIRST_NAME: 'firstName',
    LAST_NAME: 'lastName'
  }
  
  const StateExample = () => {
    const [formValues, changeFormValues] = React.useState({
      [FIELD_NAMES.FIRST_NAME]: '',
      [FIELD_NAMES.LAST_NAME]: ''
    })
  
    const handleInputChange = fieldName => e => {
      const fieldValue = e.target.value
  
      changeFormValues(prevState => ({
        ...prevState,
        [fieldName]: fieldValue
      }))
    }
  
    return (
      <div>
        <input
          type='text'
          onChange={handleInputChange(FIELD_NAMES.FIRST_NAME)}
          name={FIELD_NAMES.FIRST_NAME}
          value={formValues[FIELD_NAMES.FIRST_NAME]}
        />
        <input
          type='text'
          onChange={handleInputChange(FIELD_NAMES.LAST_NAME)}
          name={FIELD_NAMES.LAST_NAME}
          value={formValues[FIELD_NAMES.LAST_NAME]}
        />
      </div>
    )
  }
  
  export default StateExample
  
```
To update the state of a specific element by its name, use a higher order function and pass the names of the inputs across the closure before the event handler in the input is called. An anonymous function should be passed to <code>changeFormValues</code> method for changing input's state. This anonymous function returns a __NEW__ state object based on the __PREVIOUS__ one, but with an updated field.
>You might have noticed that I’ve created the <code>FIELD_NAMES</code> variable and defined an object for it in which I described the names of the inputs to use them inside the component. Try to remember and make use of this approach. If you’ll have to change the name of the input, this could be easily done it once and in one place. I guess you can admit that this is much more convenient than changing the name in each input separately. This approach can be defined as <code>ENUM</code>.

Now having a full set of tools and using the state of the components correctly, you can easily implement complex logic for your application.

Thanks for reading this huge article, cheers!