Skip to content

Commit 5011cb1

Browse files
committed
part 7 tweaks
1 parent d413a1a commit 5011cb1

File tree

12 files changed

+271
-199
lines changed

12 files changed

+271
-199
lines changed

src/content/7/en/part7a.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ The routing provided by React Router is enabled by changing the application as f
113113
import {
114114
BrowserRouter as Router,
115115
Routes, Route, Link
116-
} from "react-router-dom"
116+
} from 'react-router-dom'
117117

118118
const App = () => {
119119

@@ -136,7 +136,7 @@ const App = () => {
136136
</Routes>
137137

138138
<div>
139-
<i>Note app, Department of Computer Science 2022</i>
139+
<i>Note app, Department of Computer Science 2023</i>
140140
</div>
141141
</Router>
142142
)
@@ -151,7 +151,7 @@ Notice that, even though the component is referred to by the name <i>Router</i>,
151151
import {
152152
BrowserRouter as Router, // highlight-line
153153
Routes, Route, Link
154-
} from "react-router-dom"
154+
} from 'react-router-dom'
155155
```
156156

157157
According to the [v5 docs](https://v5.reactrouter.com/web/api/BrowserRouter):
@@ -207,7 +207,7 @@ const Notes = ({notes}) => (
207207
<ul>
208208
{notes.map(note =>
209209
<li key={note.id}>
210-
<Link to={`/notes/${note.id}`}>{note.content}</Link>
210+
<Link to={`/notes/${note.id}`}>{note.content}</Link> // highlight-line
211211
</li>
212212
)}
213213
</ul>
@@ -243,7 +243,7 @@ When a browser navigates to the URL for a specific note, for example, <i>/notes/
243243
import {
244244
// ...
245245
useParams // highlight-line
246-
} from "react-router-dom"
246+
} from 'react-router-dom'
247247

248248
const Note = ({ notes }) => {
249249
const id = useParams().id // highlight-line
@@ -385,7 +385,7 @@ const App = () => {
385385
</Router>
386386
<footer>
387387
<br />
388-
<em>Note app, Department of Computer Science 2022</em>
388+
<em>Note app, Department of Computer Science 2023</em>
389389
</footer>
390390
</div>
391391
)
@@ -439,7 +439,7 @@ The _App_component becomes:
439439
import {
440440
// ...
441441
useMatch // highlight-line
442-
} from "react-router-dom"
442+
} from 'react-router-dom'
443443

444444
const App = () => {
445445
// ...
@@ -468,7 +468,7 @@ const App = () => {
468468
</Routes>
469469

470470
<div>
471-
<em>Note app, Department of Computer Science 2022</em>
471+
<em>Note app, Department of Computer Science 2023</em>
472472
</div>
473473
</div>
474474
)

src/content/7/en/part7b.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ This part also contains a [series of exercises](/en/part7/exercises_extending_th
1515

1616
React offers 15 different [built-in hooks](https://reactjs.org/docs/hooks-reference.html), of which the most popular ones are the [useState](https://reactjs.org/docs/hooks-reference.html#usestate) and [useEffect](https://reactjs.org/docs/hooks-reference.html#useeffect) hooks that we have already been using extensively.
1717

18-
In [part 5](/en/part5/props_children_and_proptypes#references-to-components-with-ref) we used the [useImperativeHandle](https://reactjs.org/docs/hooks-reference.html#useimperativehandle) hook which allows components to provide their functions to other components.
18+
In [part 5](/en/part5/props_children_and_proptypes#references-to-components-with-ref) we used the [useImperativeHandle](https://reactjs.org/docs/hooks-reference.html#useimperativehandle) hook which allows components to provide their functions to other components. In [part 6](/en/part6/react_query_use_reducer_and_the_contex) we used [useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer) and [useContext](https://reactjs.org/docs/hooks-reference.html#usecontext) to implement a Redux like state management.
1919

20-
Within the last couple of years, many React libraries have begun to offer hook-based APIs. [In part 6](/en/part6/flux_architecture_and_redux) we used the [useSelector](https://react-redux.js.org/api/hooks#useselector) and [useDispatch](https://react-redux.js.org/api/hooks#usedispatch) hooks from the react-redux library to share our redux-store and dispatch function to our components. Redux's hook-based API is a lot easier to use than the older, still available, [connect](/en/part6/connect) API.
20+
Within the last couple of years, many React libraries have begun to offer hook-based APIs. [In part 6](/en/part6/flux_architecture_and_redux) we used the [useSelector](https://react-redux.js.org/api/hooks#useselector) and [useDispatch](https://react-redux.js.org/api/hooks#usedispatch) hooks from the react-redux library to share our redux-store and dispatch function to our components.
2121

22-
The [React Router's](https://reactrouter.com/en/main/start/tutorial) API we introduced in the [previous part](/en/part7/react_router) is also partially [hook](https://reacttraining.com/react-router/web/api/Hooks)-based. Its hooks can be used to access URL parameters and the _navigation_ object, which allows for manipulating the browser URL programmatically.
22+
The [React Router's](https://reactrouter.com/en/main/start/tutorial) API we introduced in the [previous part](/en/part7/react_router) is also partially [hook](https://reacttraining.com/react-router/web/api/Hooks)-based. Its hooks can be used to access URL parameters and the <i>navigation</i> object, which allows for manipulating the browser URL programmatically.
2323

2424
As mentioned in [part 1](/en/part1/a_more_complex_state_debugging_react_apps#rules-of-hooks), hooks are not normal functions, and when using those we have to adhere to certain [rules or limitations](https://reactjs.org/docs/hooks-rules.html). Let's recap the rules of using hooks, copied verbatim from the official React documentation:
2525

@@ -50,7 +50,7 @@ We implemented a counter application in [part 1](/en/part1/component_state_event
5050

5151
```js
5252
import { useState } from 'react'
53-
const App = (props) => {
53+
const App = () => {
5454
const [counter, setCounter] = useState(0)
5555

5656
return (
@@ -103,7 +103,7 @@ Our custom hook uses the _useState_ hook internally to create its state. The hoo
103103
React components can use the hook as shown below:
104104

105105
```js
106-
const App = (props) => {
106+
const App = () => {
107107
const counter = useCounter()
108108

109109
return (
@@ -426,7 +426,7 @@ The application is otherwise complete, but in this exercise, you have to impleme
426426

427427
Use the API endpoint [full name](https://restcountries.com/#api-endpoints-v3-full-name) to fetch a country's details in a _useEffect_ hook within your custom hook.
428428

429-
Note that in this exercise it is essential to use useEffect's [second parameter](https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect) array to control when the effect function is executed.
429+
Note that in this exercise it is essential to use useEffect's [second parameter](https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect) array to control when the effect function is executed. See the course [part 2](/en/part2/adding_styles_to_react_app#couple-of-important-remarks) for more info how the second parameter could be used.
430430

431431
#### 7.8: ultimate hooks
432432

src/content/7/en/part7c.md

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ Let's install the package with the command:
3131
npm install react-bootstrap
3232
```
3333

34-
Then let's add a link for loading the CSS stylesheet for Bootstrap inside of the <i>head</i> tag in the <i>public/index.html</i> file of the application:
34+
Then let's add a [link for loading the CSS stylesheet](https://react-bootstrap.github.io/getting-started/introduction#stylesheets) for Bootstrap inside of the <i>head</i> tag in the <i>public/index.html</i> file of the application:
3535

3636
```js
3737
<head>
3838
<link
3939
rel="stylesheet"
40-
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
41-
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
42-
crossOrigin="anonymous"
40+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
41+
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
42+
crossorigin="anonymous"
4343
/>
4444
// ...
4545
</head>
@@ -66,6 +66,8 @@ We notice that this already affected the appearance of the application. The cont
6666

6767
![browser notes app with margin spacing](../../images/7/6ea.png)
6868

69+
#### Table
70+
6971
Next, let's make some changes to the <i>Notes</i> component so that it renders the list of notes as a [table](https://getbootstrap.com/docs/4.1/content/tables/). React Bootstrap provides a built-in [Table](https://react-bootstrap.github.io/components/table/) component for this purpose, so there is no need to define CSS classes separately.
7072

7173
```js
@@ -131,7 +133,8 @@ let Login = (props) => {
131133
</Form.Group>
132134
</Form>
133135
</div>
134-
)}
136+
)
137+
}
135138
```
136139

137140
The number of components we need to import increases:
@@ -247,16 +250,7 @@ Install the library with the command
247250
npm install @mui/material @emotion/react @emotion/styled
248251
```
249252

250-
Then add the following line to the <i>head</i> tag in the <i>public/index.html</i> file. The line loads Google's font Roboto.
251-
252-
```js
253-
<head>
254-
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
255-
// ...
256-
</head>
257-
```
258-
259-
Now let's use MaterialUI to do the same modifications to the code we did earlier with bootstrap.
253+
Now let's use MaterialUI to do the same modifications to the code we did earlier with Bootstrap.
260254

261255
Render the contents of the whole application within a [Container](https://mui.com/components/container/):
262256

@@ -273,6 +267,8 @@ const App = () => {
273267
}
274268
```
275269

270+
#### Table
271+
276272
Let's start with the <i>Notes</i> component. We'll render the list of notes as a [table](https://mui.com/material-ui/react-table/#simple-table):
277273

278274
```js
@@ -504,7 +500,6 @@ Here are some other UI frameworks for your consideration. If you do not see your
504500
- <https://semantic-ui.com/>
505501
- <https://mantine.dev/>
506502
- <https://react.fluentui.dev/>
507-
- <https://storybook.js.org>
508503
- <https://www.primefaces.org/primereact/>
509504
- <https://v2.grommet.io>
510505
- <https://blueprintjs.com>
@@ -514,7 +509,6 @@ Here are some other UI frameworks for your consideration. If you do not see your
514509
- <https://master.co/>
515510
- <https://nextui.org/>
516511

517-
518512
### Styled components
519513

520514
There are also [other ways](https://blog.bitsrc.io/5-ways-to-style-react-components-in-2019-30f1ccc2b5b) of styling React applications that we have not yet taken a look at.

src/content/7/en/part7d.md

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,36 @@ We define the functionality of webpack in the <i>webpack.config.js</i> file, whi
9999
```js
100100
const path = require('path')
101101

102+
const config = () => {
103+
return {
104+
entry: './src/index.js',
105+
output: {
106+
path: path.resolve(__dirname, 'build'),
107+
filename: 'main.js'
108+
}
109+
}
110+
}
111+
112+
module.exports = config
113+
```
114+
115+
**Note:** it would be possible to make the definition directly as an object instead of a function:
116+
117+
```js
118+
const path = require('path')
119+
102120
const config = {
103121
entry: './src/index.js',
104122
output: {
105123
path: path.resolve(__dirname, 'build'),
106124
filename: 'main.js'
107125
}
108126
}
127+
109128
module.exports = config
110129
```
111130

131+
An object will suffice in many situations, but we will later need certain features that require the definition to be done as a function.
112132

113133
We will then define a new npm script called <i>build</i> that will execute the bundling with webpack:
114134

@@ -176,18 +196,20 @@ Let's take a closer look at the contents of our current <i>webpack.config.js</i>
176196
```js
177197
const path = require('path')
178198

179-
const config = {
180-
entry: './src/index.js',
181-
output: {
182-
path: path.resolve(__dirname, 'build'),
183-
filename: 'main.js'
199+
const config = () => {
200+
return {
201+
entry: './src/index.js',
202+
output: {
203+
path: path.resolve(__dirname, 'build'),
204+
filename: 'main.js'
205+
}
184206
}
185207
}
186208

187209
module.exports = config
188210
```
189211

190-
The configuration file has been written in JavaScript and the configuration object is exported using Node's module syntax.
212+
The configuration file has been written in JavaScript and the function returning the configuration object is exported using Node's module syntax.
191213

192214
Our minimal configuration definition almost explains itself. The [entry](https://webpack.js.org/concepts/#entry) property of the configuration object specifies the file that will serve as the entry point for bundling the application.
193215

@@ -268,26 +290,32 @@ We can use [loaders](https://webpack.js.org/concepts/loaders/) to inform webpack
268290
Let's configure a loader to our application that transforms the JSX code into regular JavaScript:
269291

270292
```js
271-
const config = {
272-
entry: './src/index.js',
273-
output: {
274-
path: path.resolve(__dirname, 'build'),
275-
filename: 'main.js',
276-
},
277-
// highlight-start
278-
module: {
279-
rules: [
280-
{
281-
test: /\.js$/,
282-
loader: 'babel-loader',
283-
options: {
284-
presets: ['@babel/preset-react'],
293+
const path = require('path')
294+
295+
const config = () => {
296+
return {
297+
entry: './src/index.js',
298+
output: {
299+
path: path.resolve(__dirname, 'build'),
300+
filename: 'main.js'
301+
},
302+
// highlight-start
303+
module: {
304+
rules: [
305+
{
306+
test: /\.js$/,
307+
loader: 'babel-loader',
308+
options: {
309+
presets: ['@babel/preset-react'],
310+
},
285311
},
286-
},
287-
],
288-
},
289-
// highlight-end
312+
],
313+
},
314+
// highlight-end
315+
}
290316
}
317+
318+
module.exports = config
291319
```
292320

293321
Loaders are defined under the <i>module</i> property in the <i>rules</i> array.
@@ -625,7 +653,7 @@ const App = () => {
625653

626654
### Minifying the code
627655

628-
When we deploy the application to production, we are using the <i>main.js</i> code bundle that is generated by webpack. The size of the <i>main.js</i> file is 1356668 bytes even though our application only contains a few lines of our code. The large file size is because the bundle also contains the source code for the entire React library. The size of the bundled code matters since the browser has to load the code when the application is first used. With high-speed internet connections, 1356668 bytes is not an issue, but if we were to keep adding more external dependencies, loading speeds could become an issue, particularly for mobile users.
656+
When we deploy the application to production, we are using the <i>main.js</i> code bundle that is generated by webpack. The size of the <i>main.js</i> file is 1009487 bytes even though our application only contains a few lines of our code. The large file size is because the bundle also contains the source code for the entire React library. The size of the bundled code matters since the browser has to load the code when the application is first used. With high-speed internet connections, 1009487 bytes is not an issue, but if we were to keep adding more external dependencies, loading speeds could become an issue, particularly for mobile users.
629657

630658
If we inspect the contents of the bundle file, we notice that it could be greatly optimized in terms of file size by removing all of the comments. There's no point in manually optimizing these files, as there are many existing tools for the job.
631659

@@ -691,7 +719,7 @@ Let's store the following content in the <i>db.json</i> file:
691719
692720
Our goal is to configure the application with webpack in such a way that, when used locally, the application uses the json-server available in port 3001 as its backend.
693721
694-
The bundled file will then be configured to use the backend available at the <https://obscure-harbor-49797.herokuapp.com/api/notes> URL.
722+
The bundled file will then be configured to use the backend available at the <https://notes2023.fly.dev/api/notes> URL.
695723
696724
We will install <i>axios</i>, start the json-server, and then make the necessary changes to the application. For the sake of changing things up, we will fetch the notes from the backend with our [custom hook](/en/part7/custom_hooks) called _useNotes_:
697725
@@ -716,7 +744,7 @@ const useNotes = (url) => {
716744
const App = () => {
717745
const [counter, setCounter] = useState(0)
718746
const [values, setValues] = useState([])
719-
const url = 'https://obscure-harbor-49797.herokuapp.com/api/notes' // highlight-line
747+
const url = 'https://notes2023.fly.dev/api/notes' // highlight-line
720748
const notes = useNotes(url) // highlight-line
721749

722750
const handleClick = () => {
@@ -738,34 +766,22 @@ export default App
738766
739767
The address of the backend server is currently hardcoded in the application code. How can we change the address in a controlled fashion to point to the production backend server when the code is bundled for production?
740768
741-
Let's change the configuration object in the <i>webpack.config.js</i> file to a function instead of an object:
769+
Webpack's configuration function has two parameters, <i>env</i> and <i>argv</i>. We can use the latter to find out the <i>mode</i> defined in the npm script:
742770
743771
```js
744-
const path = require('path');
772+
const path = require('path')
745773

746-
const config = (env, argv) => {
774+
const config = (env, argv) => { // highlight-line
775+
console.log('argv.mode:', argv.mode)
747776
return {
748-
entry: './src/index.js',
749-
output: {
750-
// ...
751-
},
752-
devServer: {
753-
// ...
754-
},
755-
devtool: 'source-map',
756-
module: {
757-
// ...
758-
},
759-
plugins: [
760-
// ...
761-
],
777+
// ...
762778
}
763779
}
764780

765781
module.exports = config
766782
```
767783
768-
The definition remains almost the same, except for the fact that the configuration object is now returned by the function. The function receives the two parameters, <i>env</i> and <i>argv</i>, the second of which can be used for accessing the <i>mode</i> that is defined in the npm script.
784+
Now, if we want, we can set Webpack to work differently depending on whether the application's operating environment, or <i>mode</i>, is set to production or development.
769785
770786
We can also use webpack's [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) for defining <i>global default constants</i> that can be used in the bundled code. Let's define a new global constant <i>BACKEND\_URL</i> that gets a different value depending on the environment that the code is being bundled for:
771787
@@ -778,7 +794,7 @@ const config = (env, argv) => {
778794

779795
// highlight-start
780796
const backend_url = argv.mode === 'production'
781-
? 'https://obscure-harbor-49797.herokuapp.com/api/notes'
797+
? 'https://notes2023.fly.dev/api/notes'
782798
: 'http://localhost:3001/notes'
783799
// highlight-end
784800

@@ -831,6 +847,8 @@ const App = () => {
831847
832848
If the configuration for development and production differs a lot, it may be a good idea to [separate the configuration](https://webpack.js.org/guides/production/) of the two into their own files.
833849
850+
Now, if the application is started with the command _npm start_ in development mode, it fetches the notes from the address http://localhost:3001/notes. The version bundled with the command _npm run build_ uses the address https://notes2023.fly.dev/api/notes to get the list of notes.
851+
834852
We can inspect the bundled production version of the application locally by executing the following command in the <i>build</i> directory:
835853
836854
```js

0 commit comments

Comments
 (0)