Episode 50: Refreshing React (Part 2):

Written by thatdania | Published 2018/02/09
Tech Story Tags: react | condition | thatdania | dynamics | advice

TLDRvia the TL;DR App

Note: This is all in App.js unless stated otherwise.

Rendering content conditionally:

This becomes very vital when we want to show or hide stuff, or simply write if conditions in our render part of the components. As mentioned earlier, we are simply wanting to show and hide an div element (let’s say).

What we do is that we make a state called “showPersons” which resembles true or false, and we make the method that changes it from true to false.

First, we write the render method code by actually writing javascript code in the render method (of React) with the curly brackets, as such in App.js

render() {return (<div className="App"><h1>Hello! I am now!</h1><button onClick={this.togglePersonsHandler}> Switch Name </button>{ this.state.showPersons === true ?<div><Person name={this.state.person[0].name}age={this.state.person[0].age}click={this.switchNameHandler.bind(this, 'Max!')}change={this.nameChangeHandler}/><Person name={this.state.person[1].name}age={this.state.person[1].age}>Hobbies: Disturb Dania</Person><Person name={this.state.person[2].name}age={this.state.person[2].age}/></div>:null}</div>)}

There’s a lot going on here, but we only need to notice three things.

{ this.state.showPersons === true ?...

One is this line, that begins the curly bracket. This is a one line statement asking whether the state is true or not? (It it is true, it will show the div, otherwise it will give back null.

:null

Two, as it says, below before the closing bracket. Now, three, you may be looking at this line, as this method has yet to exist yet.

<button onClick={this.togglePersonsHandler}> Switch Name </button>

We simply write this method above our render method, as a new method.

togglePersonsHandler = () => {const doesShow = this.state.showPersons;this.setState({showPersons: !doesShow})}

What is interesting about this syntax, is that we are setting the state to a constant value. We are then setting the state to whatever the constant is not

In other words, if the constant is true, the method will turn it false. If the constant is false, it will be true. What a flexible one liner!

What if the render is rendering too much:

Okay so those are great one liners but our render method looks very chunky, especially our return method. It’s so chunky we need to aid it to slim down a bit. In situations, where we could be adding a ton of show and hides, this might not be the nicest way to write code.

Solution: We can actually move that entire hide element above and out of the return method in the render method, as such.

render() {

let persons = null;

if (this.state.showPersons) {persons = (<div><Person name={this.state.person[0].name}age={this.state.person[0].age}click={this.switchNameHandler.bind(this, 'Max!')}change={this.nameChangeHandler}/><Person name={this.state.person[1].name}age={this.state.person[1].age}>Hobbies: Disturb Dania</Person><Person name={this.state.person[2].name}age={this.state.person[2].age}/></div>);}

return (  
<div className="App">  
  <h1>Hello! I am now!</h1>  
  <button onClick={this.togglePersonsHandler}> Switch Name </button>  
  {persons}  
</div>  
)  

}}

A lot going on here. Let’s go step by step.

let persons = null;

We set a variable of persons to be null at first.

if (this.state.showPersons) {persons = (...

Then below, we write and if condition that says, if the showPersons state is true, the the content of persons will change into that entire div!

render() {return (<div className=”App”><h1>Hello! I am now!</h1><button onClick={this.togglePersonsHandler}> Switch Name </button>{persons}</div>)}

Finally, in our render method, we call that variable simply with two curly brackets, known as {persons}

Mapping an array for Lists:

As you can see, I’m accomplishing multiple persons by duplicating the Persons class three times, as I’ve mentioned above.

if (this.state.showPersons) {persons = (<div><Person name={this.state.person[0].name}age={this.state.person[0].age}click={this.switchNameHandler.bind(this, 'Max!')}change={this.nameChangeHandler}/><Person name={this.state.person[1].name}age={this.state.person[1].age}>Hobbies: Disturb Dania</Person><Person name={this.state.person[2].name}age={this.state.person[2].age}/></div>);}

We can refactor this given that the state is in an array.

state = {person: [{ name:’Dania’, age:’23' },{ name:’Maxine’, age:’3' },{ name:’Mom’, age:’50' }],showPersons: false}

We simply can map over the array in a method that can be called in the if section, as such.

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person) => {return <Person name={person.name}age={person.age}/>})}</div>);}

We are mapping over the state, given the state is an array, and thus this will print the same result but it is refactored code.

Improving the flexibility of the list:

Let’s say we want to be able to delete from this array. The only way we can do that is passing the map function an index. So, to do this delete element from array…

We would make a method, call it in a click property (so that when we click on a box it disspears). We would do it as such:

deletePersonHandler = (personIndex) => {const person = this.state.person;person.splice(personIndex, 1);this.setState({person: person})}

We would first create the method that deletes a person.

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person, index) => {return <Person name={person.name}age={person.age}click={() => this.deletePersonHandler(index)}/>})}</div>);}

Then we would call the method and set it to a click property. We use the arrow function when the method takes in an argument. Don’t forget that the delete array function needs the index, so in the map function, we need to pass the person and the index.

However, there is a massive flaw in this method.

Updating State Immutably.

deletePersonHandler = (personIndex) => {const person = this.state.person;person.splice(personIndex, 1);this.setState({person: person})}

In this method, we are changing the original state which in React, is a no no. We want to duplicate the state so we have the original stored somewhere. Now, we can go two ways to solve this problem.

Either, we do this: (Old javascript syntax) Look at the 2nd line of code.

deletePersonHandler = (personIndex) => {const person = this.state.person.slice()person.splice(personIndex, 1);this.setState({person: person})}

By slicing the state to a variable, we are setting the sliced array to a variable, which is a copy of the state. Or we can do it the Javascript ES6 way, which is weird by doing this:

deletePersonHandler = (personIndex) => {const person = [...this.state.person];person.splice(personIndex, 1);this.setState({person: person})}

The three dots is called the “spread operator”, and in this case, the spread operator spreads out the elements of the array, into a separate list and into this array.

Working on the lists: (errors)

Okay, so far, everything works but we come up with these errors when we use React. Let’s work on one of them for now, and explain.

Before I click the button | After I click the button

Error message

[To my understanding]

When React executes the render method, it will compare the first result on the DOM and the version where there are changes on the DOM, and the re-render those changes to the DOM.

In our code, although in the screenshot, you can see that there are clearly three different names, React cannot see this. React can only see person components as we have written in our code.

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person, index) => {return <Person name={person.name}age={person.age}click={() => this.deletePersonHandler(index)}/>})}</div>);}

See, there is no Dania, Maxine or Mom as that is collected in the state. All we see is person components and that is what exactly what react sees.

So, the error we are getting “ the iterator should have a unique key prop” means that we need to give a key (identity) to each person version we render, so that React can make the comparison.

Although our software works, if I was rendering longer lists, it will take longer to load because its hitting against this error.

In short, we are not helping React do it’s job.

So by giving it a key, we are giving each version of “Persons” and the “states” a unique Id for React to compare versions. Now, in order to give each part of data a unique id, we can give it to state as such.

state = {person: [{ id: 'asdfw', name:'Dania', age:'23' },{ id: 'werw', name:'Maxine', age:'3' },{ id: 'dfgd', name:'Mom', age:'50' }],showPersons: false}

We first give the state an Id property

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person, index) => {return <Person name={person.name}age={person.age}key={person.id}click={() => this.deletePersonHandler(index)}/>})}</div>);}

…and then add it as a prop when we return it, by giving it a key property. You can inspect how React does this by launching the program, checking the ‘Elements’ section and watch the Person elements disappear as you click them. [In my case]

Making everything in the list dynamic:

Warning: This section may cause some time for you to process.

So far, in my case, I have only allowed one of my fill in boxes in which when you type inside it, it changes the text above. What if I want all my fields to do so…dynamically. Well…let’s start in the render function.

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person, index) => {return <Person name={person.name}age={person.age}key={person.id}change={(event) => this.nameChangeHandler(event, person.id)}click={() => this.deletePersonHandler(index)}/>})}</div>);}

In the render function, I have set the change property, to a method. Since this method takes in arguments, it needs to be an arrow function. However, we want this function to pass in an event, which we put in the first bracket so the function gets called first. In this method, we want to pass the event and the id of the person as when a user types in the field, the code knows which box field to change.

Let’s now look at the method itself.

nameChangeHandler = (event, id) => {const personIndex = this.state.person.findIndex(p => {return p.id === id;});

const persons = {...this.state.person[personIndex]}persons.name = event.target.value

const person = [...this.state.person];person[personIndex] = persons;

this.setState({ person: person})}

It gets a bit crazy with the variables but let’s go through line by line…part by part.

nameChangeHandler = (event, id) => {const personIndex = this.state.person.findIndex(p => {return p.id === id;});

The first line, we pass the event and id into this method.

The second line we make a constant variable called personIndex and we write a method inside this const that will find the person base on the index. We do this by calling the findIndex method and a true/false statement to say:

“Hey if the id of the person equals to the id given as an argument to the method, return it”

const persons = {...this.state.person[personIndex]}persons.name = event.target.value

Then, we set another constant variable. I have used person as the array of persons, so I will use persons instead (confusing naming I know).

I will then, make a copy of this state that will search the person via id.

I can then set the persons.name to the event clicker, which means the name will change when the user types it in the field.

const person = [...this.state.person];person[personIndex] = persons;

I will then make another variable, calling it person and make a copy of the original state.

I then find person via personsIndex to equal to the variable I had made earlier. This set the version of person to persons. So for example, person could have been Dania. Persons will be Maxine. I then set Maxine version of person to Dania version of person.

this.setState({ person: person})

Then, I finally set the State officially, changing the very original person state to the new state I have made.

Dynamic Styling: [ Changing a button’s color ]

Let’s start with a small case. Let’s say we want the button to turn red when the list is not shown and red when the list is shown. For this case, we will use inline styling given that we are going to add some logic to our code.

In our App.js, in our render method, we have a const variable style, with the following properties.

const style ={backgroundColor: 'green',color: 'white',font: 'inherit',border: '1px solid blue',padding: '8px',cursor: 'pointer',outline: 'none'}

This is what we call an inline style, where the styling in not separated into a CSS file. Rather, it is part of the javascript file (App.js) that we are writing in. So, right now, the style variable is setting the background Color to ‘green’.

Where are we calling this variable, well:

return (<div className="App"><h1>Hello! I am now!</h1><button style ={style}onClick={this.togglePersonsHandler}> Switch Name </button>{persons}</div>)}

We are setting the style const to the style of the button in our return method, which is inside the render method. Let’s go back to our objective.

Our objective is to make the button red when the list is shown. So, if I remember, we had an if statement which toggles the list to show and hide itself on the page, above our render method.

if (this.state.showPersons) {persons = (<div>{this.state.person.map((person, index) => {return <Person name={person.name}age={person.age}key={person.id}change={(event) => this.nameChangeHandler(event, person.id)}click={() => this.deletePersonHandler(index)}/>})}</div>);

style.backgroundColor = 'red';}

Wow, so all we had to do is call the constant “style” along with its property and change the color of it in the if statement! It is as simple as that. If you are not getting the code here…what it’s saying is:

If this.state.showpersons is true, map the state array to show the list and change the backgroundColor of the button to red. So great! We have right now dynamically styled a button, an element of CSS.

What if we wanted to dynamically adapt CSS classes?

When I mean CSS classes, I mean this:

<div className="App"></div>

How can we make this more dynamic? For example, in my program

I can delete each block by clicking on the block. Let’s say every time I delete the block, the text above the button changes. First, let’s state the conditions:

  • If there are two boxes, the font will turn red.
  • If there is only onebox, the font should be red and bold.

The technique I have used is including css classes made in a css file, that is being imported into the App.js I suppose you could do the same when passing in elements (the earlier example we did, with an element) so this is I guess another way to dynamically affect classes.

So in my App.css file, I have the classes ‘red’ and ‘bold’ as such:

.red {color: red;}

.bold {font-weight: bold;}

Going back to my App.js, I then want to use some logic/javascript to be inputted into the className of the text element.

<p className={}> This is really working!</p>

Something like this. Now, if I were to write the class names without thinking about dynamic styling, I would write it like this, I believe.

<p className='red bold'> This is really working!</p>

That sentence is what we want to input into our className, except we want some logic behind it. So this is the solution we are taught:

const classes = []if(this.state.person.length <= 2){classes.push('red');}if(this.state.person.length <= 1){classes.push('bold');}

We first make a constant variable call classes an array. We then set the first conditions where if there are less than 2 or 2 person block, the font should be red so we push the ‘red’ word into the array. The next condition is that if there is less than 1 or 1 person block, we push the bold word into the array.

Finally, the icing of the cake is that we need to join the array so we get this output : ‘red bold’ for our className to recgonise them as css classes. So:

<p className={classes.join(' ')}> This is really working!</p>

There! We join the classes with a string and volia! We have just dynamically styled css classes.

On that note, I shall end it there as that’s enough distance for the brain fuel to drain. Hopefully, not to over-drain.

Fun Fact of the Day:

Motivate me someone, please.

It’s come to my mind that I haven’t properly exercised in 4 months and it’s daunting to know that when I want to start again, it’s gonna be hella hard. However, just like sweeping away those dusty fingers, one can only start to exercise in order to move forward. Lesson learnt here is that it’s basically more worth it to keep consistent or start exercising asap than let your body, mind or soul gather dust.

If anyone is out there reading this, please I urgently need help to get my fitness level up and don’t know where to begin. I’ve suffered on maintaining my fitness for the longest time and at the age of 23, I want to overcome this hurdle of my life. I want a progressive workout, I want to do something fun (I used to dance a lot, so might start on that) and a nice butt.

If you have a share your story, can help with tips or just advise, please write it in the comments below to know as I’m currently setting out goals or options to run for this hurdle straight on.


Published by HackerNoon on 2018/02/09