Welcome to the EpicWeb.dev Workshop app!

This is the deployed version. Run locally for full experience.

Managing UI State

Dynamic applications allow users to interact and make changes to what they see on the page and the application should respond by updating the UI based on what the user has done. We accomplish this by using state. When state changes (for example as the result of user interaction and input), we update the UI. Here's how that works with React:
render --> set up the DOM --> user interacts --> state changes --> re-render --> update the DOM --> return to user interacts
From there's a cycle of user interaction, state changes, and re-rendering. This is the core of how React works for interactive applications.
The render phase is what what we've done so far with creating React elements. Handling user interactions is what we've done with event listeners like onSubmit. Now we're going to get into the state changes bit.
In React, you use special functions called "hooks" to do this. Common built-in hooks include:
  • useState
  • useRef
  • use
  • useReducer
  • useEffect
Each of these is a special function that you can call inside your custom React component function to store data (like state) or perform actions (or side-effects). There are a few more built-in hooks that have special use cases, but the ones above are what you'll be using most of the time.
Each of the hooks has a unique API. Some return a value (like useRef and use), others return a pair of values (like useState and useReducer), and others return nothing at all (like useEffect).
Here's an example of a component that uses the useState hook and an onClick event handler to update that state:
function Counter() {
	const [count, setCount] = useState(0)
	const increment = () => setCount(count + 1)
	return <button onClick={increment}>{count}</button>
}
useState is a function that accepts a single argument. That argument is the initial state for the instance of the component. In our case, the state will start as 0.
useState returns a pair of values. It does this by returning an array with two elements (and we use destructuring syntax to assign each of those values to distinct variables). The first of the pair is the state value and the second is a function we can call to update the state. We can name these variables whatever we want. Common convention is to choose a name for the state variable, then prefix set in front of that for the updater function.
State can be defined as: data that changes over time. So how does this work over time? When the button is clicked, our increment function will be called at which time we update the count by calling setCount.
When we call setCount, that tells React to re-render our component. When it does this, the entire Counter function is re-run, so when useState is called this time, the value we get back is the value that we called setCount with. And it continues like that until Counter is unmounted (removed from the application), or the user closes the application.
Note that after the initial render, the argument passed to useState is ignored. The only time it's used is when the component is first created. The only way to change the state value of the component while it's around is to call the updater function returned by useState.
๐Ÿ“œ To get a deeper dive on how React keeps track of hooks, read/watch this great post/talk by Shawn Wang: Getting Closure on Hooks
๐Ÿ“œ And here's a reference to the hooks official documentation.