5 - Fullstack Part2 - Adding Styles To React App
5 - Fullstack Part2 - Adding Styles To React App
Let's take a look at how we can add styles to a React application. There are several different ways of
doing this and we will take a look at the other methods later on. First, we will add CSS to our
application the old-school way; in a single file without using a CSS preprocessor (although this is not
entirely true as we will learn later on).
Let's add a new index.css file under the src directory and then add it to the application by importing it
in the main.jsx file:
h1 { copy
color: green;
}
CSS rules comprise of selectors and declarations. The selector defines which elements the rule
should be applied to. The selector above is h1, which will match all of the h1 header tags in our
application.
One CSS rule can contain an arbitrary number of properties. Let's modify the previous rule to make
the text cursive, by defining the font style as italic:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 1/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
h1 { copy
color: green;
font-style: italic;
}
There are many ways of matching elements by using different types of CSS selectors .
If we wanted to target, let's say, each one of the notes with our styles, we could use the selector li, as
all of the notes are wrapped inside li tags:
return (
<li>
{note.content}
<button onClick={toggleImportance}>{label}</button>
</li>
)
}
Let's add the following rule to our style sheet (since my knowledge of elegant web design is close to
zero, the styles don't make much sense):
li { copy
color: grey;
padding-top: 3px;
font-size: 15px;
}
Using element types for defining CSS rules is slightly problematic. If our application contained other
li tags, the same style rule would also be applied to them.
If we want to apply our style specifically to notes, then it is better to use class selectors .
In regular HTML, classes are defined as the value of the class attribute:
In React we have to use the className attribute instead of the class attribute. With this in mind,
let's make the following changes to our Note component:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 2/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
return (
<li className='note'>
{note.content}
<button onClick={toggleImportance}>{label}</button>
</li>
)
}
.note { copy
color: grey;
padding-top: 5px;
font-size: 15px;
}
If you now add other li elements to the application, they will not be affected by the style rule above.
We previously implemented the error message that was displayed when the user tried to toggle the
importance of a deleted note with the alert method. Let's implement the error message as its
own React component.
return (
<div className='error'>
{message}
</div>
)
}
If the value of the message prop is null , then nothing is rendered to the screen, and in other
cases, the message gets rendered inside of a div element.
Let's add a new piece of state called errorMessage to the App component. Let's initialize it with some
error message so that we can immediately test our component:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 3/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
// ...
return (
<div>
<h1>Notes</h1>
<Notification message={errorMessage} />
<div>
<button onClick={() => setShowAll(!showAll)}>
show {showAll ? 'important' : 'all' }
</button>
</div>
// ...
</div>
)
}
.error { copy
color: red;
background: lightgrey;
font-size: 20px;
border-style: solid;
border-radius: 5px;
padding: 10px;
margin-bottom: 10px;
}
Now we are ready to add the logic for displaying the error message. Let's change the
toggleImportanceOf function in the following way:
noteService
.update(id, changedNote).then(returnedNote => {
setNotes(notes.map(note => note.id !== id ? note : returnedNote))
})
.catch(error => {
setErrorMessage(
`Note '${note.content}' was already removed from server`
)
setTimeout(() => {
setErrorMessage(null)
https://fullstackopen.com/en/part2/adding_styles_to_react_app 4/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
}, 5000)
setNotes(notes.filter(n => n.id !== id))
})
}
When the error occurs we add a descriptive error message to the errorMessage state. At the
same time, we start a timer, that will set the errorMessage state to null after five seconds.
The code for the current state of our application can be found in the part2-7 branch on GitHub .
Inline styles
React also makes it possible to write styles directly in the code as so-called inline styles .
The idea behind defining inline styles is extremely simple. Any React component or element can be
provided with a set of CSS properties as a JavaScript object through the style attribute.
CSS rules are defined slightly differently in JavaScript than in normal CSS files. Let's say that we
wanted to give some element the color green and italic font that's 16 pixels in size. In CSS, it would
look like this:
{ copy
color: green;
font-style: italic;
font-size: 16px;
}
{ copy
color: 'green',
https://fullstackopen.com/en/part2/adding_styles_to_react_app 5/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
fontStyle: 'italic',
fontSize: 16
}
Every CSS property is defined as a separate property of the JavaScript object. Numeric values for
pixels can be simply defined as integers. One of the major differences compared to regular CSS, is
that hyphenated (kebab case) CSS properties are written in camelCase.
Next, we could add a "bottom block" to our application by creating a Footer component and defining
the following inline styles for it:
return (
<div>
<h1>Notes</h1>
// ...
<Footer />
</div>
)
}
Inline styles come with certain limitations. For instance, so-called pseudo-classes can't be used
straightforwardly.
Inline styles and some of the other ways of adding styles to React components go completely against
the grain of old conventions. Traditionally, it has been considered best practice to entirely separate
CSS from the content (HTML) and functionality (JavaScript). According to this older school of
thought, the goal was to write CSS, HTML, and JavaScript into their separate files.
The philosophy of React is, in fact, the polar opposite of this. Since the separation of CSS, HTML, and
JavaScript into separate files did not seem to scale well in larger applications, React bases the
division of the application along the lines of its logical functional entities.
https://fullstackopen.com/en/part2/adding_styles_to_react_app 6/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
The structural units that make up the application's functional entities are React components. A React
component defines the HTML for structuring the content, the JavaScript functions for determining
functionality, and also the component's styling; all in one place. This is to create individual
components that are as independent and reusable as possible.
The code of the final version of our application can be found in the part2-8 branch on GitHub .
Exercises 2.16.-2.17.
Use the improved error message example from part 2 as a guide to show a notification that lasts for
a few seconds after a successful operation is executed (a person is added or a number is changed):
Open your application in two browsers. If you delete a person in browser 1 a short while before
attempting to change the person's phone number in browser 2, you will get the following error
messages:
Fix the issue according to the example shown in promise and errors in part 2. Modify the example so
that the user is shown a message when the operation does not succeed. The messages shown for
https://fullstackopen.com/en/part2/adding_styles_to_react_app 7/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
Note that even if you handle the exception, the first "404" error message is still printed to the
console. But you should not see "Uncaught (in promise) Error".
At the end of this part there are a few more challenging exercises. At this stage, you can skip the
exercises if they are too much of a headache, we will come back to the same themes again later. The
material is worth reading through in any case.
We have done one thing in our app that is masking away a very typical source of error.
// ...
}
This is a pretty natural initial value since the notes are a set, that is, there are many notes that the
state will store.
If the state would be only saving "one thing", a more proper initial value would be null denoting
that there is nothing in the state at the start. Let us try what happens if we use this initial value:
// ...
}
https://fullstackopen.com/en/part2/adding_styles_to_react_app 8/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
The error message gives the reason and location for the error. The code that caused the problems is
the following:
// ...
{notesToShow.map(note =>
<Note key={note.id} note={note} />
)}
The variable notesToShow is first assigned the value of the state notes and then the code tries
to call method map to a nonexisting object, that is, to null .
The effect hook uses the function setNotes to set notes to have the notes that the backend is
returning:
However the problem is that the effect is executed only after the first render. And because notes
has the initial value of null:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 9/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
// ...
// ...
and this blows up the app since we can not call method map of the value null .
When we set notes to be initially an empty array, there is no error since it is allowed to call map
to an empty array.
So, the initialization of the state "masked" the problem that is caused by the fact that the data is not
yet fetched from the backend.
Another way to circumvent the problem is to use conditional rendering and return null if the
component state is not properly initialized:
useEffect(() => {
noteService
.getAll()
.then(initialNotes => {
setNotes(initialNotes)
})
}, [])
// ...
}
So on the first render, nothing is rendered. When the notes arrive from the backend, the effect used
function setNotes to set the value of the state notes . This causes the component to be
rendered again, and at the second render, the notes get rendered to the screen.
https://fullstackopen.com/en/part2/adding_styles_to_react_app 10/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
The method based on conditional rendering is suitable in cases where it is impossible to define the
state so that the initial rendering is possible.
The other thing that we still need to have a closer look is the second parameter of the useEffect:
The second parameter of useEffect is used to specify how often the effect is run . The principle is
that the effect is always executed after the first render of the component and when the value of the
second parameter changes.
If the second parameter is an empty array [] , its content never changes and the effect is only run
after the first render of the component. This is exactly what we want when we are initializing the app
state from the server.
However, there are situations where we want to perform the effect at other times, e.g. when the state
of the component changes in a particular way.
Consider the following simple application for querying currency exchange rates from the Exchange
rate API :
useEffect(() => {
console.log('effect run, currency is now', currency)
https://fullstackopen.com/en/part2/adding_styles_to_react_app 11/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
const onSearch = (event) => {
event.preventDefault()
setCurrency(value)
}
return (
<div>
<form onSubmit={onSearch}>
currency: <input value={value} onChange={handleChange} />
<button type="submit">exchange rate</button>
</form>
<pre>
{JSON.stringify(rates, null, 2)}
</pre>
</div>
)
}
The user interface of the application has a form, in the input field of which the name of the desired
currency is written. If the currency exists, the application renders the exchange rates of the currency
to other currencies:
The application sets the name of the currency entered to the form to the state currency at the
moment the button is pressed.
When the currency gets a new value, the application fetches its exchange rates from the API in
the effect function:
useEffect(() => {
https://fullstackopen.com/en/part2/adding_styles_to_react_app 12/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
console.log('effect run, currency is now', currency)
The useEffect hook now has [currency] as the second parameter. The effect function is therefore
executed after the first render, and always after the table as its second parameter [currency]
changes. That is, when the state currency gets a new value, the content of the table changes and
the effect function is executed.
if (currency) { copy
// exchange rates are fetched
}
which prevents requesting the exchange rates just after the first render when the variable
currency still has the initial value, i.e. a null value.
So if the user writes e.g. eur in the search field, the application uses Axios to perform an HTTP GET
request to the address https://open.er-api.com/v6/latest/eur and stores the response in the rates
state.
When the user then enters another value in the search field, e.g. usd, the effect function is executed
again and the exchange rates of the new currency are requested from the API.
The way presented here for making API requests might seem a bit awkward. This particular
application could have been made completely without using the useEffect, by making the API
requests directly in the form submit handler function:
However, there are situations where that technique would not work. For example, you might
encounter one such a situation in the exercise 2.20 where the use of useEffect could provide a
https://fullstackopen.com/en/part2/adding_styles_to_react_app 13/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
solution. Note that this depends quite much on the approach you selected, e.g. the model solution
does not use this trick.
Exercises 2.18.-2.20.
The user interface is very simple. The country to be shown is found by typing a search query into the
search field.
If there are too many (over 10) countries that match the query, then the user is prompted to make
their query more specific:
If there are ten or fewer countries, but more than one, then all countries matching the query are
shown:
When there is only one country matching the query, then the basic data of the country (eg. capital
and area), its flag and the languages spoken are shown:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 14/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
NB: It is enough that your application works for most countries. Some countries, like Sudan, can be
hard to support since the name of the country is part of the name of another country, South Sudan.
You don't need to worry about these edge cases.
There is still a lot to do in this part, so don't get stuck on this exercise!
Improve on the application in the previous exercise, such that when the names of multiple countries
are shown on the page there is a button next to the name of the country, which when pressed shows
the view for that country:
In this exercise, it is also enough that your application works for most countries. Countries whose
name appears in the name of another country, like Sudan, can be ignored.
Add to the view showing the data of a single country, the weather report for the capital of that
country. There are dozens of providers for weather data. One suggested API is
https://fullstackopen.com/en/part2/adding_styles_to_react_app 15/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
https://openweathermap.org . Note that it might take some minutes until a generated API key is
valid.
If you use Open weather map, here is the description for how to get weather icons.
NB: In some browsers (such as Firefox) the chosen API might send an error response, which
indicates that HTTPS encryption is not supported, although the request URL starts with http:// .
This issue can be fixed by completing the exercise using Chrome.
NB: You need an api-key to use almost every weather service. Do not save the api-key to source
control! Nor hardcode the api-key to your source code. Instead use an environment variable to save
the key.
Assuming the api-key is 54l41n3n4v41m34rv0, when the application is started like so:
export VITE_SOME_KEY=54l41n3n4v41m34rv0 && npm run dev // For Linux/macOS Bash copy
($env:VITE_SOME_KEY="54l41n3n4v41m34rv0") -and (npm run dev) // For Windows PowerShell
set "VITE_SOME_KEY=54l41n3n4v41m34rv0" && npm run dev // For Windows cmd.exe
you can access the value of the key from the import.meta.env object:
https://fullstackopen.com/en/part2/adding_styles_to_react_app 16/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
Note that you will need to restart the server to apply the changes.
This was the last exercise of this part of the course. It's time to push your code to GitHub and mark
all of your finished exercises to the exercise submission system .
Part 2d Part 3
Previous part Next part
About course
Course contents
FAQ
Partners
Challenge
https://fullstackopen.com/en/part2/adding_styles_to_react_app 17/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
https://fullstackopen.com/en/part2/adding_styles_to_react_app 18/19
2/20/24, 2:34 PM Fullstack part2 | Adding styles to React app
https://fullstackopen.com/en/part2/adding_styles_to_react_app 19/19