Skip to content

Commit 429d2ef

Browse files
committed
Not sure about the Event Handlers article
Need a more realistic example
1 parent 1092d78 commit 429d2ef

File tree

2 files changed

+62
-79
lines changed

2 files changed

+62
-79
lines changed

apps/components_guide_web/lib/components_guide_web/templates/react_typescript/_top.html.eex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<ul y-y x-x=md class="text-lg list-none font-bold" data-links="p-3">
1212
<li><%= link("Fundamentals", to: '/react+typescript') %>
1313
<li><%= link("Testing", to: '/react+typescript/testing') %>
14-
<li><%= link("Event Handlers", to: '/react+typescript/event-handlers') %>
14+
<li hidden><%= link("Event Handlers", to: '/react+typescript/event-handlers') %>
1515
<li><%= link("Logical Clocks", to: '/react+typescript/logical-clocks') %>
1616
<li><%= link("Forms", to: '/react+typescript/forms') %>
1717
</ul>
Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,74 @@
11
# React Event Handlers
22

3-
## Deciding whether to control a form element
3+
## The Problem
44

5-
- A controlled element lets you have **full control** over its value.
6-
- An uncontrolled element can let you handle events with less code.
5+
We have a link that is associated with a campaign, and when someone clicks the link we want to record that the campaign received some interest.
76

8-
## Controlled form elements
9-
10-
- We store the state for each field using `useState`.
11-
- We must set the current `value` of the input when rendering.
12-
- We must listen to when the user changes using `onChange` on each input, and update our state.
13-
- We can then read our state when the form is submitted.
7+
Here’s how we might write that:
148

159
```tsx
16-
import React, { useState } from "react";
17-
import authService from "../services/auth";
18-
19-
function SignInForm() {
20-
const [email, updateEmail] = useState("");
21-
const [password, updatePassword] = useState("");
22-
23-
return (
24-
<form
25-
onSubmit={(event) => {
26-
event.preventDefault(); // Prevent performing normal submission
27-
// Could validate here.
28-
authService.signIn({ email, password });
29-
}}
30-
>
31-
<label>
32-
Email
33-
<input
34-
type="email"
35-
value={email}
36-
onChange={(event) => {
37-
updateEmail(event.target.value);
38-
}}
39-
/>
40-
</label>
41-
<label>
42-
Password
43-
<input
44-
type="password"
45-
value={password}
46-
onChange={(event) => {
47-
updatePassword(event.target.value);
48-
}}
49-
/>
50-
</label>
51-
<button type="submit">Sign In</button>
52-
</form>
53-
);
10+
type AuthStatus = 'SignedIn' | 'SignedOut';
11+
12+
function trackCampaignClick(campaign: string, authStatus: AuthStatus) {
13+
// Probably make some sort of HTTP request…
14+
}
15+
16+
function TwitterLink({ campaign }: { campaign: string }) {
17+
const authStatus = useAuthStatus(); // Reads from context.
18+
19+
return <a
20+
href="https://twitter.com/royalicing"
21+
onClick={() => {
22+
// When a click happens, we record two things:
23+
// the current campaign, and whether the user is signed in or not.
24+
trackCampaignClick(campaign, authStatus);
25+
}}
26+
>
27+
Follow me on Twitter
28+
</a>;
5429
}
5530
```
5631

57-
## Uncontrolled form elements
32+
However, even for our simple link, there’s an issue. What happens if either the campaign or auth status changes?
5833

59-
- We have no state — the input itself holds the state.
60-
- We could set an initial value using `defaultValue`.
61-
- We don’t have to listen to any change events.
62-
- We can then read from the form using the DOM when it is submitted.
34+
We receive `campaign` as a prop — this could change at any time as it is our parent providing it, and we don’t where this value comes from and how often it changes. (This example of a campaign is admittedly unlikely to change often)
6335

64-
```tsx
65-
import React, { useState } from "react";
66-
import authService from "../services/auth";
67-
68-
function SignInForm() {
69-
return (
70-
<form
71-
onSubmit={(event) => {
72-
event.preventDefault(); // Prevent performing normal submission
73-
const email = event.target.elements.email.value;
74-
const password = event.target.elements.password.value;
75-
// Could validate here.
76-
authService.signIn({ email, password });
77-
}}
78-
>
79-
<label>
80-
Email
81-
<input type="email" name="email" />
82-
</label>
83-
<label>
84-
Password
85-
<input type="password" name="password" />
86-
</label>
87-
<button type="submit">Sign In</button>
88-
</form>
89-
);
36+
And `useAuthStatus()` is also out of control. We don’t know when the user signs in or out.
37+
38+
So in order for our `trackCampaignClick(campaign, authStatus)` call to be accurate, we need to keep these values up to date. There’s no much point in recording metrics if they use stale data!
39+
40+
## Solution 1: Create a new Event handler when its deps change
41+
42+
```js
43+
function TwitterLink({ campaign }: { campaign: string }) {
44+
const authStatus = useAuthStatus(); // Reads from context.
45+
46+
const onClick = useCallback(() => {
47+
// When a click happens, we record two things:
48+
// the current campaign, and whether the user is signed in or not.
49+
trackCampaignClick(campaign, authStatus);
50+
}, [campaign, authStatus]);
51+
52+
return <a href="https://twitter.com/royalicing" onClick={onClick}>
53+
Follow me on Twitter
54+
</a>;
55+
}
56+
```
57+
58+
## Solution 2: Store changing state in a ref
59+
60+
## Solution 3: Put State in the DOM and read it from the Event handler
61+
62+
## Solution 4: Don’t store the state in React
63+
64+
## Solution 5: Put a unique key in the DOM and read state from a shared store
65+
66+
----
67+
68+
An example implementation of `trackCampaignClick()`:
69+
70+
```js
71+
function trackCampaignClick(campaign) {
72+
navigator.sendBeacon('/analytics', new URLSearchParams({ campaign }));
9073
}
9174
```

0 commit comments

Comments
 (0)