React useState

up:: React Hooks

Tldr

Standard JS variables don’t trigger a component rerender. When you need components to update, useState is your friend.

After the first run, if you ever want a change to the DOM, you need to use state.

  • State can also be used through useContext,

  • or by triggering a state change in a parent through React props.

  • If the parent component changes state, all the children components also get re-evaluated

  • React goes through JSX function after function, down the inheritance tree until there are no more components left. Once it is done, it resolves the application in the DOM. Then it is done. It does not go through the components again.

    • That’s where state comes in
// Import useState
import React, { useState } from 'react';
 
const Item = (props) => {
	// Assign the setter and getter function
	// Assign the default variable
	const [title, setTitle] = useState(props.title);
	// We used array destructuring for assignment there
 
	const clickHandler = () => {
		// Use the setter function on a click
		setTitle("Updated");
	}
}
 return (
	// Use the assigned state variable
	<h2>{title}</h2>
	// Call the handler function on a click
	<button onClick={clickHandler}>Change title</button>
 )
 
export default Item;
  • useState returns an array that always has two values: the first is the initial value, the second is a function to update the value
  • Using useState in a component marks it as a component that should be recomputed later

Each on their own

useState reevaluates each component instance separately.

If the same component repeats, not all of them change if one does.

Multiple state slices

  • You can have multiple states per component
  • This is okay:
  const [enteredTitle, setEnteredTitle] = useState("");
  const [enteredAmount, setEnteredAmount] = useState("");
  const [enteredDate, setEnteredDate] = useState("");
 
  const titleChangeHandler = (event) => {
    setEnteredTitle(event.target.value);
  };
  const amountChangeHandler = (event) => {
    setEnteredAmount(event.target.value);
  };
  const dateChangeHandler = (event) => {
    setEnteredDate(event.target.value);
  }; 

Multiple states in an object

As as different approach, you can also assign the states as an object

const ExpenseForm = () => {
  const [userInput, setUserInput] = useState({
    enteredTitle: '',
    enteredAmount: '',
    enteredDate: '',
  })
  const titleChangeHandler = (event) => {
    setUserInput({
      ...userInput,
      enteredTitle: event.target.value,
    })
  };
  const amountChangeHandler = (event) => {
    setUserInput({
      ...userInput,
      enteredAmount: event.target.value,
    });
  };
  const dateChangeHandler = (event) => {
    setUserInput({
      ...userInput,
      enteredInput: event.target.value,
    });
  };
 

Values can get lost

When setting the new key value pair in the object, existing data gets lost. You need to use the Spread Operator to copy over the existing data.

Common practice

According to the instructor, using the individual slices is more common.

Depending on previous states

Depending on previous state

Whenever you depend on the previous state, you need to pass in the previous state. Since React schedules states, things can get tangled up

const titleChangeHandler = (event) => {
	setUserInput((prevState) => {
		return { ...prevState, enteredTitle: event.target.value };
	});
}

Two way binding

  • Listen for the value and set the value
  • This works with setting value
  • This is useful in forms
// in the titleChangeHandler() function
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
 
// in the component
<input
	type="text"
	value={enteredTitle}
	onChange={titleChangeHandler}
/>