Photo by Karsten Winegeart on Unsplash

Understanding Local State with React Hooks

Spotswood

--

A simple walkthrough for sorting an array using local state and React hooks.

If you’re trying to understand local state in React, I hope this walkthrough serves you well. I was recently challenged to create a button on my React.js / Rails application that sorts an array of players by their last name using only local state. After searching around, asking a few specific questions, and finding the right documentation, I was able to conquer the challenge. Huzzah!

High-Level Overview

My application lists all batters from the MLB in the 2021 season. Originally, I was grabbing state from Redux in the functional components that needed it. Instead of using state from Redux, I set up local state for the component with the useState React hook. Take a look:

const playersInRedux = useSelector((state) => state.players)
const [players, setPlayers] = useState(playersInRedux)

There was no way around initially getting my players from the Redux store. That’s where all my data was after all. The second line in the code above creates the local state. Before starting this challenge, my understanding of local state and hooks was subpar. This challenge enabled me to learn how to make better use of hooks like useState. Hooks do exactly what they imply: they allow us to hook into React features. The state variable declared for useState returns two values: the current state and a function that updates it. It takes only the initial state as an argument. In the code above, the initial state is contained in the playersInRedux variable.

Now let’s get on to the sorting portion of the code. To access sorted players and not change the original state, I would need to store these sorted players in another state variable:

const [isAlphabetized, setIsAlphabetized] = useState(false)

The initial state is passed in as a boolean for a reason. We don’t want to use it immediately when the page renders. Our sorting function will handle whether or not we render the alphabetized state on the page or the original state. Here’s the function that sorts the players and sets the state:

const sortPlayers = () => {
if (isAlphabetized) {
setPlayers(playersInRedux)
} else {
const sortedPlayers = [...players].sort((a, b) => {
if (a.name < b.name) {
return -1
}
if (a.name > b.name) {
return 1
}
return 0
})
setPlayers(sortedPlayers)
}
setIsAlphabetized(!isAlphabetized)
}

You can create multiple states inside a functional component using the useState hook. The function above is toggling back and forth between the different states when our button is clicked. Here’s the code where we are calling the sortPlayers function so you can get an idea of the big picture:

return (
<div className="player-container">
<h2 className="text-center">All Players</h2><br></br>
<button onClick={sortPlayers}>Alphabetize</button>
<div className="d-flex justify-content-center align-items-center container">
<div className="align-self-center row row-cols-1 row-cols-md-3 g-4">
{players.map(p =>
<div key={p.id} className="col">
<div className="card" style={{width: '18rem'}}>
<div className="card-body">
<h5 className="card-title">{p.name}</h5>
<button type="button" className="btn btn-primary"><Link className="link-light" to={`${p.id}/hits`}>See More Stats</Link></button>
</div>
</div>
</div>
)}
</div>
</div>
</div>
)

Remember, the page will not re-render unless changes are made to the DOM. Our Alphabetize button is calling the sortPlayers function, which chooses which state to use, updates the state, and returns the new state. This function causes the DOM to re-render with the updated state.

Diving Deeper

That’s the high-level explanation of the button and its functionality. Let’s look at the code that allows us to achieve our desired outcome of sorting the players by their last name.

Spread Operator

The spread operator and sort function are utilized in this line of code:

const sortedPlayers = [...players].sort((a, b) => {
if (a.name < b.name) {
return -1
}
if (a.name > b.name) {
return 1
}
return 0
})

Taken step by step, we can see that the sortedPlayers variable is set equal to the players array which we call .sort on. We do this because we don’t want to change the original array of players. We want to store the sorted players array in a variable.

The spread operator (...) allows us to use the initial state and update it without changing the original players array. This makes state immutable. Exactly what we need! The spread operator allows us to make a copy of the JavaScript object. The rest of the code is a piece of cake once you spend some time with it.

.sort JavaScript Function

Sorting an object in JavaScript calls for the .sort function. This function takes two arguments: the first comparison element and the second comparison element. Here, the function converts the players’ name elements into strings and compares the code units in order, returning a sorted array.

Future Improvement

To improve the efficiency of the code, I wanted to use a ternary operator instead of a long if statement. Here’s the finished sortPlayers function that passes the challenge:

const sortPlayers = () => {
if (isAlphabetized) {
setPlayers(playersInRedux)
} else {
const sortedPlayers = [...players].sort((a, b) => {
return ((a.name === b.name) ? 0 : ((a.name > b.name)? 1: -1));
})
setPlayers(sortedPlayers)
}
setIsAlphabetized(!isAlphabetized)
}

This exercise helped solidify my knowledge of local state, the spread operator, sorting JavaScript objects, and ternary operators. I hope it helps you as well. Thank you for taking the time to read.

Resources

Github repository for this application

React.js Documentation

JavaScript Documentation

Spread Operator

JavaScript Sort Function

--

--

Spotswood
Spotswood

Written by Spotswood

I write about software development projects in JavaScript (React), Ruby on Rails, R, and other languages 🧱🏗👷🏻‍♂️

No responses yet