Skip to content

Commit 61d6cd4

Browse files
Addressed some more feedback.
1 parent a7c5e01 commit 61d6cd4

1 file changed

Lines changed: 47 additions & 29 deletions

File tree

pages/tutorials/React.md

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ create-react-app my-app --scripts-version=react-scripts-ts
2828

2929
[react-scripts-ts](https://www.npmjs.com/package/react-scripts-ts) is a set of adjustments to take the standard create-react-app project pipeline and bring TypeScript into the mix.
3030

31-
At this point, your project layout should more or less look something like the following:
31+
At this point, your project layout should look like the following:
3232

3333
```text
3434
my-app/
@@ -58,7 +58,7 @@ Running the project is as simple as running
5858
npm run start
5959
```
6060

61-
This runs the `start` script specified in our `package.json`, and will spawn off a server reacts to updates as we save our files.
61+
This runs the `start` script specified in our `package.json`, and will spawn off a server which reloads the page as we save our files.
6262
Typically the server runs at `http://localhost:3000`, but should be automatically opened for you.
6363

6464
This tightens the iteration loop by allowing us to quickly preview changes.
@@ -77,9 +77,9 @@ If you'd like, you can run `npm run start` and `npm run test` side by side so th
7777

7878
# Creating a production build
7979

80-
When running the project (as specified earlier), we didn't end up with an optimized build.
80+
When running the project with `npm run start`, we didn't end up with an optimized build.
8181
Typically, we want the code we ship to users to be as fast and small as possible.
82-
Certain optimizations (like minification) can accomplish this, but often take more time.
82+
Certain optimizations like minification can accomplish this, but often take more time.
8383
We call builds like this "production" builds (as opposed to development builds).
8484

8585
To run a production build, just run
@@ -90,7 +90,8 @@ npm run build
9090

9191
This will create an optimized JS and CSS build in `./build/static/js` and `./build/static/css` respectively.
9292

93-
You won't need to run a production build most of the time, but it's often useful to do so before committing changes.
93+
You won't need to run a production build most of the time,
94+
but it is useful if you need to measure things like the final size of your app.
9495

9596
# Creating a component
9697

@@ -113,15 +114,15 @@ export interface Props {
113114
enthusiasmLevel?: number;
114115
}
115116

116-
const Hello = ({ name, enthusiasmLevel = 1 }: Props) => {
117+
function Hello({ name, enthusiasmLevel = 1 }: Props) {
117118
if (enthusiasmLevel <= 0) {
118119
throw new Error('You could be a little more enthusiastic. :D');
119120
}
120121

121122
return (
122123
<div className="hello">
123124
<div className="greeting">
124-
Hello {name + getExclamationMarks(enthusiasmLevel)}!
125+
Hello {name + getExclamationMarks(enthusiasmLevel)}
125126
</div>
126127
</div>
127128
);
@@ -132,21 +133,22 @@ export default Hello;
132133
// helpers
133134

134135
function getExclamationMarks(numChars: number) {
135-
return Array(numChars).join('!');
136+
return Array(numChars + 1).join('!');
136137
}
137138
```
138139

139140
Notice that we defined a type named `Props` that specifies the properties our component will take.
140141
`name` is a required `string`, and `enthusiasmLevel` is an optional `number` (which you can tell from the `?` that we wrote out after its name).
141142

142143
We also wrote `Hello` as a stateless function component (an SFC).
143-
To be specific, `Hello` is a variable being assigned an arrow function.
144-
That arrow function destructures a given `Props` object, and defaults `enthusiasmLevel` to `1` if it isn't defined.
144+
To be specific, `Hello` is a function that takes a `Props` object, and destructures it.
145+
If `enthusiasmLevel` isn't given in our `Props` object, it will default to `1`.
145146

147+
Writing functions is one of two primary [ways React allows us to make components]((https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components)).
146148
If we wanted, we *could* have written it out as a class as follows:
147149

148150
```ts
149-
class Hello extends React.Component<Props, undefined> {
151+
class Hello extends React.Component<Props, object> {
150152
render() {
151153
const { name, enthusiasmLevel = 1 } = this.props;
152154

@@ -157,20 +159,20 @@ class Hello extends React.Component<Props, undefined> {
157159
return (
158160
<div className="hello">
159161
<div className="greeting">
160-
Hello {name + getExclamationMarks(enthusiasmLevel)}!
162+
Hello {name + getExclamationMarks(enthusiasmLevel)}
161163
</div>
162164
</div>
163165
);
164166
}
165167
}
166168
```
167169

168-
But we don't really need to think about state in this example - in fact, we specified it as `undefined` in `React.Component<Props, undefined>`, so writing an SFC tends to be shorter.
170+
But we don't really need to think about state in this example - in fact, we specified it as `object` in `React.Component<Props, object>`, so writing an SFC tends to be shorter.
169171
We will revisit how to bind global application state with Redux in a bit, but local component state is more useful at the presentational level when creating generic UI elements that can be shared between libraries.
170172

171-
Now that we've written our component, let's replace our render of `<App />` with a render of `<Hello ... />`.
173+
Now that we've written our component, let's dive into `index.tsx` and replace our render of `<App />` with a render of `<Hello ... />`.
172174

173-
First we'll import it.
175+
First we'll import it at the top of the file:
174176

175177
```ts
176178
import Hello from './components/Hello.tsx';
@@ -206,7 +208,7 @@ To style our `Hello` component, we can create a CSS file at `src/components/Hell
206208
}
207209
```
208210

209-
The tools that create-react-app uses (namely, Webpack and various loaders) allow us to simply import the stylesheets we're interested in.
211+
The tools that create-react-app uses (namely, Webpack and various loaders) allow us to import the stylesheets we're interested in.
210212
So in `src/components/Hello.tsx`, we'll add the following import.
211213

212214
```ts
@@ -224,9 +226,10 @@ Let's reiterate what they were:
224226
225227
We can use these requirements to write a few tests for our components.
226228

227-
But first, let's add our first dependency.
228-
Enzyme is a common tool in the React ecosystem that makes it easier to write tests for how components will behave.
229-
While we have jsdom support available to emulate how a component will act in the DOM, Enzyme makes it easier to make certain queries about our components.
229+
But first, let's install Enzyme.
230+
[Enzyme](http://airbnb.io/enzyme/) is a common tool in the React ecosystem that makes it easier to write tests for how components will behave.
231+
By default, our application includes a library called jsdom to allow us to simulate the DOM and test its runtime behavior without a browser.
232+
Enzyme is similar, but builds on jsdom and makes it easier to make certain queries about our components.
230233

231234
Let's install it as a development-time dependency.
232235

@@ -236,10 +239,12 @@ npm install -D enzyme @types/enzyme react-addons-test-utils
236239

237240
Notice we installed packages `enzyme` as well as `@types/enzyme`.
238241
The `enzyme` package refers to the package containing JavaScript code that actually gets run, while `@types/enzyme` is a package that contains declaration files (`.d.ts` files) so that TypeScript can understand how you can use Enzyme.
242+
You can learn more about `@types` packages [here](https://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html).
243+
239244
We also had to install `react-addons-test-utils`.
240-
`enzyme` actually expects `jsdom` and `react-addons-test-utils` to be installed, but we already have the former, and the latter may be optional for older versions of React.
245+
This is something `enzyme` expects to be installed.
241246

242-
Now that we've got Enzyme installed, let's start writing our test!
247+
Now that we've got Enzyme set up, let's start writing our test!
243248
Let's create a file named `src/components/Hello.test.tsx`, adjacent to our `Hello.tsx` file from earlier.
244249

245250
```ts
@@ -292,16 +297,15 @@ As far as a React component is concerned, data flows down through its children t
292297

293298
As an answer, the React community relies on libraries like Redux and MobX.
294299

295-
[Redux](http://redux.js.org) relies on synchronizing data through a centralized immutable store of data, and updates to that data will trigger a re-renders to parts of the application.
300+
[Redux](http://redux.js.org) relies on synchronizing data through a centralized and immutable store of data, and updates to that data will trigger a re-render our application.
296301
State is updated in an immutable fashion by sending explicit action messages which must be handled by functions called reducers.
297-
Because of the explicit nature, it may often be easier to reason about how an action will affect the state of your program.
302+
Because of the explicit nature, it is often be easier to reason about how an action will affect the state of your program.
298303

299304
[MobX](https://mobx.js.org/) relies on functional reactive patterns where state is wrapped through observables and and passed through as props.
300-
State is updated in a very natural way through traditional assignments that one would typically write in JavaScript.
301305
Keeping state fully synchronized for any observers is done by simply marking state as observable.
302306
As a nice bonus, the library is already written in TypeScript.
303307

304-
Both have different advantages and merits, with certain tradeoffs.
308+
There are various merits and have tradeoffs to both.
305309
Generally Redux tends to see more widespread usage, so for the purposes of this tutorial, we'll focus on adding Redux;
306310
however, you should feel encouraged to explore both.
307311

@@ -389,7 +393,7 @@ We've created two types that describe what increment actions and decrement actio
389393
We also created a type (`EnthusiasmAction`) to describe cases where an action could be an increment or a decrement.
390394
Finally, we made two functions that actually manufacture the actions.
391395

392-
There's some clear boilerplate here, so you should feel free to look into libraries like [redux-actions](https://www.npmjs.com/package/redux-actions) once you've got the hang of things.
396+
There's clearly boilerplate here, so you should feel free to look into libraries like [redux-actions](https://www.npmjs.com/package/redux-actions) once you've got the hang of things.
393397

394398
## Adding a reducer
395399

@@ -421,7 +425,9 @@ Notice that we're using the *object spread* (`...state`) which allows us to crea
421425
It's important that the `enthusiasmLevel` property come last, since otherwise it would be overridden by the property in our old state.
422426

423427
You may want to write a few tests for your reducer.
424-
It should be relatively easy. since reducers are pure functions, and can be passed arbitrary data.
428+
Since reducers are pure functions, they can be passed arbitrary data.
429+
For every input, reducers can tested by checking their newly produced state.
430+
Consider looking into Jest's [toEqual](https://facebook.github.io/jest/docs/expect.html#toequalvalue) method to accomplish this.
425431

426432
## Making a container
427433

@@ -451,7 +457,7 @@ function Hello({ name, enthusiasmLevel = 1, onIncrement, onDecrement }: Props) {
451457
return (
452458
<div className="hello">
453459
<div className="greeting">
454-
Hello {name + getExclamationMarks(enthusiasmLevel)}!
460+
Hello {name + getExclamationMarks(enthusiasmLevel)}
455461
</div>
456462
<div>
457463
<button onClick={onDecrement}>-</button>
@@ -562,7 +568,7 @@ const store = createStore<StoreState>(enthusiasm, {
562568

563569
`store` is, as you might've guessed, our central store for our application's global state.
564570

565-
Next, we're going to swap our our use of `./src/components/Hello` with `./src/containers/Hello` and use react-redux's `Provider` to wire up our props with our container.
571+
Next, we're going to swap our use of `./src/components/Hello` with `./src/containers/Hello` and use react-redux's `Provider` to wire up our props with our container.
566572
We'll import each:
567573

568574
```ts
@@ -583,6 +589,18 @@ ReactDOM.render(
583589

584590
Notice that `Hello` no longer needs props, since we used our `connect` function to adapt our application's state for our wrapped `Hello` component's props.
585591

592+
### Type assertions
593+
594+
One final thing we'll point out in this section is the line `document.getElementById('root') as HTMLElement`.
595+
This syntax is called a *type assertion*, often also just called a *cast*.
596+
This is a useful way of telling TypeScript that you know a little more about the type than the type checker does.
597+
598+
The reason we need to do so in this case is that `getElementById`'s return type is `HTMLElement | null`, meaning that it can return `null`.
599+
We're operating under the assumption that `getElementById` will actually succeed, so we need convince TypeScript of that using the `as` syntax.
600+
601+
TypeScript also has a trailing "bang" (`!`) syntax, which removes `null` and `undefined` from the prior expression.
602+
So we *could* have written `document.getElementById('root')!`, but in this case we wanted to be a bit more explicit.
603+
586604
# Ejecting
587605

588606
If at any point, you feel like there are certain customizations that the create-react-app setup has made difficult, you can always opt-out and get the various configuration options you need.

0 commit comments

Comments
 (0)