a
Rendering a collection, modules
Before starting a new part, let's add some additional tools to our IDE and quickly remind ourselves about arrays and event handlers. For now, you can continue by opening up the previous lab that we used in part 1.
New Tool: WebStorm Live Templates
With WebStorm it's easy to create and live templates, i.e., shortcuts/snippets
for quickly generating commonly re-used portions of code, much like how sout
works in NetBeans and psvm
works in IntelliJ.
Try erasing everything in App.jsx, then type rsc
and then hit the Tab key and see the magic that appears!
Instructions for working with live templates can be found here. The main idea is that in certain contexts, you would be able to type a shortcut, and WebStorm will provide you with some code that you would normally write.
There are already lots of live templates that are available for you to review in Settings->Editor->Live Templates. Specifically, you can find the rsc template in the React section. You should also peruse the React hooks section to see things like useState.
One popular live template/shortcut is log
, which replaces having to type out console.log()
.
To use it, type log
and hit Tab to autocomplete.
Though it doesn't stop there, as you can make custom live templates.
For example, I'm going to take this tutorial and reduce it to its essence and apply it to our new template.
Let's set up a custom clog
live template.
- Go to Settings->Editor->Live Templates
- With Javascript Selected, click on the
+
icon, and select 1 Live Template - Type
clog
for the Abbreviation andMore detailed console.log
for the Description -
Paste this in the template text:
console.log('$PARAM_TEXT$(' + typeof $PARAM$ + ') =', $PARAM$, ' | $FILE$:$LINE$ - $EXPECTED$')$END$
- Select Reformat according to style
- Click the Define link below the template text and select Javascript & Typescript.
-
Finally, click Edit variables... and make it look like this, make sure you have the order of the variables as well, where
PARAM
comes first.- Use the up and down buttons highlighted to move the sequence of the variables.

Click OK twice and then try it out by typing clog
inside your App
Javascript function and then Enter.
Then type the name of the variable props
(or whatever other variable you have) and Tab again.
You should notice that it will display the word you typed in two places on that line.
First, as the name in the string for the log
statement and then as the variable to print.
Finally, if you have some expected value of what you think it should be at that point, you can type that in as well, and then Enter one last time.
You'll notice that you have this template that will provide you with a varialbe, its type, the filename and line number to make it easier to debug!
JavaScript Arrays
From here on out, we will be using JavaScript's functional programming conventions (e.g. find
, filter
, and map
) for
Arrays,
all of the time.
They operate on the same general principles as their equivalents in other languages and as streams in Java.
If operating arrays with functional operators makes you feel uneasy, it is worth watching at least the first three parts of the YouTube video series Functional Programming in JavaScript:
Event Handlers Revisited
Event handling is complex.
It's worth reviewing the previous section on event handlers.
Passing event handlers to the child components of the App
component can also be complex, so you may want to revisit
the material that was provided about the topic here.
Rendering Collections
We will now do the 'frontend', or the browser-side application logic, in React for an application that's similar to the example application from part 0
Let's start with the following in App.jsx:
const App = (props) => {
const { tasks } = props
return (
<div>
<h1>Tasks</h1>
<ul>
<li>{tasks[0].content}</li>
<li>{tasks[1].content}</li>
<li>{tasks[2].content}</li>
</ul>
</div>
)
}
export default App
We'll also need this in main.jsx:
import ReactDOM from 'react-dom/client'
import App from './App'
const tasks = [
{
id: 1,
content: 'Wash the dishes',
date: '2023-01-10T17:30:31.098Z',
important: true
},
{
id: 2,
content: 'Take out the trash',
date: '2023-01-10T18:39:34.091Z',
important: false
},
{
id: 3,
content: 'Buy salty snacks',
date: '2023-01-10T19:20:14.298Z',
important: true
}
]
ReactDOM.createRoot(document.getElementById('root')).render(
<App tasks={tasks} />
)
Every task contains:
- a unique
id
. content
describing the task- a
date
, - a boolean for marking whether the task is
important
The example above works only because there are exactly three tasks in the array.
A single task is rendered by accessing the objects in the array by referring to a hard-coded index number:
<li>{tasks[1].content}</li>
This is, of course, not elegant.
We can improve on this by generating React elements from the array objects using the
map
function.
tasks.map(task => <li>{task.content}</li>)
The result is an array of li
elements.
[
<li>Wash the dishes</li>,
<li>Take out the trash</li>,
<li>Buy salty snacks</li>,
]
Which can then be placed inside ul
tags:
const App = (props) => {
const { tasks } = props
return (
<div>
<h1>Tasks</h1>
<ul> {tasks.map(task => <li>{task.content}</li>)} </ul> </div>
)
}
Because the code generating the li
tags is JavaScript, it must be wrapped in curly braces in a JSX template just like all other JavaScript code.
We will also make the code more readable by separating the arrow function's declaration across multiple lines:
const App = (props) => {
const { tasks } = props
return (
<div>
<h1>Tasks</h1>
<ul>
{tasks.map(task =>
<li> {task.content} </li> )}
</ul>
</div>
)
}
Key-attribute
Even though the application seems to be working, there is a warning in both WebStorm:

and the console:

As the linked React page in the error message suggests;
the list items, i.e. the elements generated by the map
method, must each have a unique key value: an attribute called key
.
Let's add the keys:
const App = (props) => {
const { tasks } = props
return (
<div>
<h1>Tasks</h1>
<ul>
{tasks.map(task =>
<li key={task.id}> {task.content}
</li>
)}
</ul>
</div>
)
}
And the error message disappears.
React uses the key attributes of objects in an array to determine how to update the view generated by a component when the component is re-rendered. More about this is in the React documentation.
Map
Understanding how the array method
map
works is crucial for the rest of the course.
Let's revisit the tasks
array that is currently in main.jsx:
const tasks = [
{
id: 1,
content: 'Wash the dishes',
date: '2023-01-10T17:30:31.098Z',
important: true
},
{
id: 2,
content: 'Take out the trash',
date: '2023-01-10T18:39:34.091Z',
important: false
},
{
id: 3,
content: 'Buy salty snacks',
date: '2023-01-10T19:20:14.298Z',
important: true
}
]
If the following code is added at the end of main.jsx:
const result = tasks.map(task => task.id)
console.log(result)
An array [1, 2, 3]
will be viewable in the console.
map
always creates a new array, the elements of which have been created from the elements of the original array by mapping:
using the function given as a parameter to the map
method.
The function in our example is:
task => task.id
This is an arrow function written in compact form. The full form would be:
(task) => {
return task.id
}
The function gets a task object as a parameter and returns the value of its id
field.
Changing our map
line to have the parameter:
const result = tasks.map(task => task.content)
results in the console containing the array ['Wash the dishes', 'Take out the trash', 'Buy salty snacks']
.
This is already pretty close to the React code we used:
tasks.map(task =>
<li key={task.id}>
{task.content}
</li>
)
This code generated a li
tag containing the contents of the task from each task object.
Because the function parameter passed to this map
method -
task => <li key={task.id}>{task.content}</li>
is used to create view elements, the value of the variable must be rendered inside curly braces. What happens if one or more of the braces are removed? Play around with removing and re-adding braces for the task in App.jsx.
The use of curly braces will cause some pain in the beginning, but you will get used to them soon enough. The visual feedback from React is immediate.
Anti-pattern: Array Indexes as Keys
We could have made the error message on our console disappear by using the array indexes as keys.
The indexes can be retrieved by passing a second parameter to the callback function of the map
method:
tasks.map((task, i) => ...)
When called like this, i
is assigned the value of the index of the position in the array where the task resides.
As such, one way to define the row generation without getting errors is:
<ul>
{tasks.map((task, i) =>
<li key={i}>
{task.content}
</li>
)}
</ul>
This is, however, 🐞 not recommended 🐞 and can create undesired problems even if it seems to be working just fine.
Read more about this in this article.
Refactoring Modules
Let's tidy the code up a bit.
We are only interested in the field tasks
from props
, so let's retrieve that directly using
destructuring:
const App = ({ tasks }) => { return (
<div>
<h1>Tasks</h1>
<ul>
{tasks.map(task =>
<li key={task.id}>
{task.content}
</li>
)}
</ul>
</div>
)
}
If you have forgotten what destructuring means and how it works, please review the section on destructuring.
We'll separate displaying a single task into its own component Task
:
const Task = ({ task }) => { return ( <li>{task.content}</li> )}
const App = ({ tasks }) => {
return (
<div>
<h1>Tasks</h1>
<ul>
{tasks.map(task => <Task key={task.id} task={task} /> )} </ul>
</div>
)
}
Notice that the key
attribute must now be defined in the Task
component, and not in the li
tags like before.
A whole React application can be written in a single file. Although that is, of course, not very practical. Common practice is to declare each component in its own file as an ES6 module.
We have been using modules the whole time. These lines from main.jsx:
import ReactDOM from 'react-dom/client'
import App from './App'
import
two modules, enabling them to be used in that file.
The module react-dom/client
is placed into the variable ReactDOM
,
and the module that defines the main component of the app is placed into the variable App
Let's move our Task
component into its own module.
In smaller applications, components are usually placed in a directory called components, which is in turn placed within the src directory. The convention is to name the file after the component.
Create a directory in src called components for our reading application and place a file named Task.jsx inside. If WebStorm asks about adding the file, make sure to check not to ask again if you haven't done so already. So reading/src/components/Task.jsx should have the following:
const Task = ({ task }) => {
return (
<li>{task.content}</li>
)
}
export default Task
The last line of the module
exports
the declared module, the variable Task
.
Now the file that is using the component - App.jsx - can import the module:
import Task from './components/Task'
const App = ({ tasks }) => {
// ...
}
You can try typing import
in the file and then Tab to see how WebStorm will handle auto-completing the line for you.
The component exported by the module is now available for use in the variable Task
, just as it was earlier.
Notice that when importing our own components, their location must be given in relation to the importing file:
'./components/Task.jsx'
The period (.
) at the start refers to the current directory,
so the module's location is a file called Task.jsx in the components sub-directory of the current directory.
The filename extension .jsx can be omitted.
Modules have plenty of other uses other than enabling component declarations to be separated into their own files. We will get back to them later in this course.
The current code of the application can be found on GitHub.
Notice that the main branch of the repository is largely empty. The current code is in the branch part2-1:

Remember: If you clone the project, run the command
npm i
before starting the application withnpm run dev
.
Using console.log
Important: for this section you may want to skip it and read it after you have started working on the exercises. You do not need to type this section into the reading folder. If you do, then please revert your changes once this section is complete.
Early in your programming career (and even after 25+ years of coding like yours truly), what can happen is that an application can just break down. This is even more so the case with dynamically typed languages, such as JavaScript, where the compiler does not check the data type. Like with function variables or return values.
A "React explosion" can, for example, look like this:

In these situations, your best way out is to either debug the program via the debugger or commands like console.log
.
The piece of code causing the explosion is this:
const Company = ({ company }) => (
<div>
<Header company={company} />
</div>
)
const App = () => {
const company = {
name: "Sample Company",
}
return (
<div>
<Company company={company} />
</div>
)
}
We'll hone in on the reason for the breakdown by adding console.log
commands to the code.
Because the first thing to be rendered is the App
component, it's worth putting a console.log
in there.
Use your clog
live template that we created above to get some easy information in, since clog
wants variables,
type company
and then Enter to type what you expect it to have.
Then, press Enter again.
For me, I did something like this.
const App = () => {
const company = {
// ...
}
console.log('File: App.js, Function: App, Line 24 - Is company (JS Object)?: ', company);
return (
// ..
)
}
To see the printing in the console, we must scroll up over the long red wall of errors.

When one thing is found to be working, it's time to dig deeper. If the component has been declared as a single statement or a function without a return, it makes printing to the console harder.
const Company = ({ company }) => (
<div>
<Header company={company} />
</div>
)
The component should be changed to its longer form for us to add the printing:
const Company = ({ company }) => {
console.log(company) return (
<div>
<Header company={company} />
</div>
)
}
Quite often the root of the problem is either:
- the props are expected to be of a different type
- the props are called with a different name than how they are passed in
So destructuring fails as a result.
The problem often begins to solve itself when destructuring is removed and we see what the props
contain.
const Company = (props) => { console.log(props) const { company } = props
return (
<div>
<Header company={company} />
</div>
)
}
If the problem has still not been resolved, then I would consider digging deeper by leveraging WebStorm's powerful debugger that we covered in part 1. You can also get help from search tools and LLMs to help you understand the errors, just not to produce solutions.
As you continue through, please remember the pledge you took to help you improve as a web developer.
FYI: If you see errors in ESLint related to props validation and you are unsure what they are, you may want to go back and look at the explanation about those errors in part 1 Also: The original author added this chapter to the material after the model answer for the next question exploded completely (due to
props
being of the wrong type), and it had to be debugged usingconsole.log
.