When migrating from vanilla JavaScript to React, I regretted the loss of the DOM manipulation skills I worked hard to develop during the JavaScript module of the Flatiron curriculum. However, while working on my final React/Redux project, I was able to learn how to conditionally render using state
and the ternary operator in my render()
as a way to continue to manipulate the DOM without having to refresh the page.
To begin, unless you’re using React hooks, you will need to be in a class component in order to set the state. In the example we’re going to use here, it is from the Trades
component of my trade journal app. Originally, the props.trades
were handed down from the parent Home
container component. The Trades
component was more of a presentational component, which rendered all the trade entries from the database and displayed them in order of the database-generated id
. A simple this.props.trades.map
was used to iterate over the objects (trades) in the trades array and present them onto the screen.
However, what if we wanted a button that would display the trades in alphabetical order of their ticker symbol (which is a string)? Moreover, what if we wanted to merely hit the button again and have it un-sorted? Let’s see…
First, since we’re in a class component, we can use our local state
. Let’s start out by declaring a state with a simple boolean data type:
state = {
toggle: false
}
Next, we will create a <button>
somewhere in the render()
to trigger a state change:
<button onClick={this.handleToggle}>ReSort</button>
And you can already see that the next step will be to create a function within our class to “handle the toggle.”
handleToggle = () => {
this.setState(prevState => ({
toggle : !prevState.toggle
})
)
}
There are a few interesting things happening in this simple function. We are using our React .setState()
function to change the state
which is proper React form. Indeed, although one can alter state
without using .setState()
, convention keeps us from doing so in order to avoid any surprises or uncontroller behavior. Also, by using prevState
we are making sure that we are updating the correct version of state
which may or may not already be updated since React updates this.state
as well as this.props
asynchronously. So, as written, handleToggle()
is taking the boolean value of toggle
and updating it with its opposite value.
Next, we need our alphabetical sort
feature which we can take directly from the MDN website here:
// sort by name
items.sort(function(a, b) {
var nameA = a.name.toUpperCase(); // ignore upper and lowercase
var nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
Since we want to return an array, we need to modify this code a bit. First, lets name it: alphaSort
and then change the var
to let
since var
has a lot of extra hoisting baggage attached to it. Also, replace their items
variable with our own this.props.trades
. Then, we need to actually return an array, so we wrap the entire function into a return
statement. However, if we put a debugger
on our page, we will notice that we can’t un-sort this.props.trades
- this is not what we want. We want to be able to toggle it back and forth to its original state. What went wrong? Unlike .map
, .sort
is destructive. It remedy this: the spred operator. [...this.props.trades].sort
is the way to accomplish this. So now our function looks like this:
alphaSort = () => {
return (
[...this.props.trades].sort(function(a, b) {
let nameA = a.ticker.toUpperCase(); // ignore upper and lowercase
let nameB = b.ticker.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
}));
}
The tricky part of this comes now at the point of conditionally rendering the results. Originally in our render()
the iteration looked like this:
<ul>
{this.props.trades.map(trade =>
<li key={trade.id}>
<Link to={`/trades/${trade.id}`}>{trade.ticker} - {trade.strategy}</Link>
</li>)}
</ul>
In order to get the correct this.props.trade
array to iterate over, we make the following change preceding the .map
statement:
(this.state.toggle ? this.alphaSort() : this.props.trades).map
Our ternary operator lets us use an If/Then statement in JSX to now easily render conditionally onto the page.
I hope this helps make for easier conditional rendering and toggling in your React code.