To Render Or Not To Render

To Render Or Not To Render

So after seeing your beautiful contact list, a few of your billionaire friends were pretty impressed, and sent you a photo to add to the list.

But not all of them. Some friends are a little more privacy conscious.

This presents a problem: how would you add an <img> tag to some contacts, but not to others?

#React is just JavaScript

Just as you’ll use for loops to create lists, you can use if statements to decide what to render!

For example, here’s how you’d use if to render a greeting appropriate to the current hour:

index.js
import React from 'react'
import ReactDOM from 'react-dom'

let isMorning = new Date().getHours() < 12

let greeting
if (isMorning) {
  greeting = React.createElement(
    'h1',
    {
      style: { color: 'green' },
    },
    "Good Morning, Dave."
  )
}
else {
  greeting = React.createElement(
    'h1',
    {
      style: { color: 'turquoise' },
    },
    "Hello, Dave."
  )
}

ReactDOM.render(
  greeting,
  document.getElementById('root')
)
Build In Progress

#The pictures

Now that you know how to handle conditional rendering, it’s time to put your knowledge to practice!

Your task is to render an <img> tag within the Contact-avatar div for each billionaire that has a photoURL defined — without rendering unnecessary <img> tags for other contacts.

The example’s styles will display an <img> tag over the top of the initials. This ensures that on a slow connection, initials will be displayed until the image can be loaded. As such, in the case that photoURL is defined, you’ll want to render both the initials and an <img> tag. As a hint, remember that you can pass multiple children to createElement() by passing 4th and subsequent arguments.

index.js
billionaires.js
styles.css
import React from 'react'
import ReactDOM from 'react-dom'
import './styles.css'
import { billionaires } from './billionaires.js'

let elements = []
for (let i = 0; i < billionaires.length; i++) {
  let contact = billionaires[i]
  let names = contact.name.split(' ')
  let initials = names.map(name => name[0].toUpperCase()).join('')
  let element =
    <div className='Contact' key={i}>
      <div className='Contact-avatar'>{initials}</div>
      <span className='Contact-name'>{contact.name}</span>
      <a href={'mailto:'+contact.email}>
        {contact.email}
      </a>
    </div>
  elements.push(element)
}

ReactDOM.render(
  <div className='ContactList'>{elements}</div>,
  document.getElementById('root')
)
Build In Progress

If you’ve given the exercise a decent crack, and would like to check your answer against mine, then go ahead! Just click the Solution button at the bottom of the editor.

And once you’re happy that you’ve got conditional rendering with if figured out, I’ll let you in on a little secret…

There’s an easier way of doing this.

#Rendering null and undefined

Way back when I first introduced React.createElement(), I mentioned three types that you could pass in as children:

  • arrays
  • strings
  • elements

In fact, there are another three that I missed:

  • null
  • undefined
  • false

So what happens when you render null, undefined or false? Let’s find out with an experiment!

index.js
import { createElement } from 'react'
import ReactDOM from 'react-dom'

let element = createElement(
  'div',
  {},

  createElement(
    'div',
    {
      style: { backgroundColor: 'red', width: 100, height: 100 }
    },
    
    null,
    'h',
    undefined,
    'i',
    false,
    '!',
  ),
)

ReactDOM.render(
  element,
  document.getElementById('root')
)
Build In Progress

Even though the above example specifies null, undefined and false as children, the only things that are rendered are the letters “hi!”. You can right click and “Inspect” the red squre to confirm for yourself.

Keeping in mind that the result of undefined && anything is always undefined, you now have a neat trick for conditional rendering:

// This will only hold an element if `condition` is true.
let element =
  condition &&
  createElement(
    // ...
  )

Your final task for this lesson is to simplify the below contact list with this trick. You should be able to reduce the length of the code by 10 lines or so.

index.js
billionaires.js
styles.css
import React from 'react'
import ReactDOM from 'react-dom'
import './styles.css'
import { billionaires } from './billionaires.js'

let elements = []
for (let i = 0; i < billionaires.length; i++) {
  let contact = billionaires[i]
  let names = contact.name.split(' ')
  let initials = names.map(name => name[0].toUpperCase()).join('')
  let avatar
  if (contact.photoURL) {
    avatar =
      <div className='Contact-avatar'>
        {initials}
        <img src={contact.photoURL} />
      </div>
  }
  else {
    avatar = <div className='Contact-avatar'>{initials}</div>
  }
  
  let element =
    <div className='Contact' key={i}>
      {avatar}
      <span className='Contact-name'>
        {contact.name}
      </span>
      <a href={"mailto:"+contact.email}>
        {contact.email}
      </a>
    </div>
  elements.push(element)
}

ReactDOM.render(
  <div className='ContactList'>{elements}</div>,
  document.getElementById('root')
)
Build In Progress

Once you’re happy with the result, click through to the next lesson!

Progress to next section.