hooks allow us to use lifecycle methods and state inside functional components.

useState

pass the initial value of state into useState function. it returns an array of the form [currentState, setState]. you can use array destructuring to get the state out of the result:

import React, {useState} from "react"

function App() {
    const [count, setCount] = useState(0)
    
    function increment() {
        setCount(prevCount => prevCount + 1)
    }
    
    return (
        <div>
            <h1>{count}</h1>
            <button onClick={increment}>Increment</button>
        </div>
    )
}

why return an array instead of an object? this allows us to name the output of useState however we want.

you can store state as a primitive or as an object.

if your state is an object, the setState function does not automatically merge whatever properties you provide into the rest of the state. you need to keep track of all the properties yourself; because of this it may be easier to keep the different properties separate:

function App() {	
	// rather than storing state as an object, keep a bunch of primitives
	const [count, setCount] = useState(0)
	const [answer, setAnswer] = useState("Yes")
	
	// ...
}

useEffect

useEffect is a replacement for lifecycle methods componentDidMount, componentDidUpdate, componentWillUnmount. essentially, it introduces side effects for things like network requests or DOM manipulation.

for useEffect, we pass in (i) a function to run whenever certain variables change, and (ii) the set of variables to keep track of:

function App() {
	const [color, setColor] = useState(randomcolor())
	
	useEffect(() => {
	  // this code will run whenever `count` changes
		setColor(randomColor())
	}, [count])

	return (
		<h1 style={{ color }}> {count} </h1>
	)
}

to just run the provided function on mount, pass in an empty array [] as the second argument.

the function you pass into useEffect can actually return something; whatever it returns will be called by React when the component is about to unmount. this means you can run whatever cleanup code you want in a function that is returned by the argument to use effect:

useEffect(() => {
	// sets up an interval timer on mount
  const intervalId = setInterval(() => {
      setCount(prevCount => prevCount + 1)
  }, 1000)
	// clears the interval timer on unmount
  return () => clearInterval(intervalId)
}, [])

refs

refs are used to get access to specific DOM elements and manipulate them imperatively.

in theory, we could just access a DOM node by giving it an id and calling document.getElementById, but this doesn't work if your component is reused in several places.

to-do list example: