Skip to content

Commit 6ab6b38

Browse files
moT01zairahira
andauthored
feat(curriculum): add react forms transcripts (freeCodeCamp#59621)
Co-authored-by: Zaira <[email protected]>
1 parent 287fc6f commit 6ab6b38

File tree

2 files changed

+353
-4
lines changed

2 files changed

+353
-4
lines changed

curriculum/challenges/english/25-front-end-development/lecture-working-with-forms-in-react/67d1a928ae86929a85c1bb6b.md

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,111 @@
22
id: 67d1a928ae86929a85c1bb6b
33
title: How Do Forms Work in React?
44
challengeType: 11
5-
videoId: nVAaxZ34khk
5+
videoId: t9ax7VY00m0
66
dashedName: how-do-forms-work-in-react
77
---
88

99
# --description--
1010

11-
Watch the video lecture and answer the questions below.
11+
Watch the video or read the transcript and answer the questions below.
12+
13+
# --transcript--
14+
15+
How do forms work in React?
16+
17+
Forms are fundamental to every web application because they let you handle user input, collect data, and trigger actions.
18+
19+
In React, forms are managed using state or refs, giving you full control over their behavior and validation. These two ways to manage forms are called "controlled" and "uncontrolled" input.
20+
21+
Let's look at what controlled and uncontrolled inputs are.
22+
23+
Controlled input is the most "React-like" way to handle form inputs. With controlled inputs, you store the input field value in state and update it through `onChange` events. This gives you complete control over the form data and allows instant validation and conditional rendering.
24+
25+
The process works like this: React maintains the form state with the `useState` hook, and you update it on every change. When a user types in an input field, the `onChange` event fires, updates the state, and React re-renders the component with the new value.
26+
27+
```js
28+
import { useState } from "react";
29+
30+
function App() {
31+
const [name, setName] = useState("");
32+
33+
const handleChange = (e) => {
34+
setName(e.target.value);
35+
};
36+
37+
const handleSubmit = (e) => {
38+
e.preventDefault();
39+
console.log(name);
40+
};
41+
42+
return (
43+
<>
44+
<form onSubmit={handleSubmit}>
45+
<label htmlFor="name">Your name</label> <br />
46+
<input value={name} id="name" onChange={handleChange} type="text" />
47+
<button type="submit">Submit</button>
48+
</form>
49+
</>
50+
);
51+
}
52+
53+
export default App;
54+
```
55+
56+
The benefits of controlled inputs include the following:
57+
58+
- Immediate access to the form data.
59+
60+
- You can implement instant validation.
61+
62+
- You can conditionally disable the submit button.
63+
64+
- You can control the input value programmatically.
65+
66+
Uncontrolled inputs on the other hand are seen more in traditional HTML forms. So, instead of handling the inputs through the `useState` hook, uncontrolled inputs in HTML maintain their own internal state with the help of the DOM.
67+
68+
Since the DOM controls the input values, what you need is to pull in the values of the input fields with ref. This approach requires less code and performs better because refs do not make React re-render.
69+
70+
Here's an example of uncontrolled inputs:
71+
72+
```js
73+
import { useRef } from "react";
74+
75+
function App() {
76+
const nameRef = useRef();
77+
78+
const handleSubmit = (e) => {
79+
e.preventDefault();
80+
console.log(nameRef.current.value);
81+
};
82+
83+
return (
84+
<form onSubmit={handleSubmit}>
85+
<label htmlFor="name">Your</label>{" "}
86+
<input type="text" ref={nameRef} id="name" />
87+
<button type="submit">Submit</button>
88+
</form>
89+
);
90+
}
91+
92+
export default App;
93+
```
94+
95+
One very noticeable advantage of uncontrolled inputs is that they require less code. They also perform better and feel more natural to React beginners who are familiar with HTML.
96+
97+
So, which should you use between controlled and uncontrolled inputs? 
98+
99+
Use controlled inputs when you need dynamic form updates, real-time validation, or when you want to sync input values with state. They provide better control but require more re-renders.
100+
101+
Use uncontrolled inputs when you need simpler forms, want to access values only on submission, or when you're working with non-React code.
102+
103+
Regardless of which you use between controlled and uncontrolled inputs, here are some best practices you should adhere to while making forms in React:
104+
105+
- Always prevent the default form submission.
106+
107+
- Ensure you validate inputs before submission.
108+
109+
- Always provide clear feedback to users with loading, validation errors or other related states.
12110

13111
# --questions--
14112

curriculum/challenges/english/25-front-end-development/lecture-working-with-forms-in-react/67e2a4cab99d4e8bc795e99d.md

Lines changed: 253 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,264 @@
22
id: 67e2a4cab99d4e8bc795e99d
33
title: What Is the useActionState Hook, and How Does It Work?
44
challengeType: 11
5-
videoId: nVAaxZ34khk
5+
videoId: GARlXz-vIFg
66
dashedName: what-is-the-useactionstate-hook-and-how-does-it-work
77
---
88

99
# --description--
1010

11-
Watch the lecture video and answer the questions below.
11+
Watch the video or read the transcript and answer the questions below.
12+
13+
# --transcript--
14+
15+
What is the `useActionState` hook and how does it work?
16+
17+
React 19 came with two notable new features called server components and server actions. 
18+
19+
From that version onwards, server components became the default in frameworks like Next.js that readily support them.
20+
21+
Server actions on the other hand, are functions that run on the server to allow form handling right on the server without the need for API endpoints.
22+
23+
A server action looks like this:
24+
25+
```js
26+
"use server";
27+
28+
async function submitForm(formData) {
29+
const name = formData.get("name");
30+
return { message: `Hello, ${name}!` };
31+
}
32+
```
33+
34+
This server action extracts a `name` field from a form and returns a string greeting that name.
35+
36+
To simplify state management for server actions and remove the need for client-side JavaScript for simple forms, the React team introduced the `useActionState` hook in version 19.
37+
38+
Let's take a closer look at this hook and see how it works.
39+
40+
The React documentation describes the `useActionState` hook as a hook that "allows you to update state based on the result of a form action."
41+
42+
But this doesn't mean that you can only use the `useActionState` hook with forms. You can also use it to manage button clicks and other events, as long as you have an action in place.
43+
44+
And keep in mind that, since `useActionState` is a hook, you cannot use it inside a server component.
45+
46+
Here's the basic syntax of the `useActionState` hook:
47+
48+
```js
49+
const [state, action, isPending] = useActionState(actionFunction, initialState, permalink);
50+
```
51+
52+
- `state` is the current state the action returns.
53+
54+
- `action` is the function that triggers the server action.
55+
56+
- `isPending` is a boolean that indicates whether the action is currently running or not.
57+
58+
- The `actionFunction` parameter is the server action itself.
59+
60+
- `initialState` is the parameter that represents the starting point for the state before the action runs.
61+
62+
- `permalink` is an optional string that contains the unique page URL the form modifies.
63+
64+
To use the `useActionState` hook, make sure you have an action in place first. Let's use the action from the previous example for this, with a bit of a twist:
65+
66+
```js
67+
"use server";
68+
69+
export async function submitForm(_, formData) {
70+
const name = formData.get("name");
71+
72+
const hour = new Date().getHours();
73+
let greeting;
74+
75+
if (hour < 12) {
76+
greeting = "Good morning";
77+
} else if (hour < 18) {
78+
greeting = "Good afternoon";
79+
} else {
80+
greeting = "Good evening";
81+
}
82+
83+
return { message: `${greeting}, ${name}` };
84+
}
85+
```
86+
87+
In your component, you then need to import the `useActionState` hook and call it at the top level of the component body (before the return statement) just like other hooks. You should also import the action:
88+
89+
```js
90+
"use client";
91+
92+
// Import the useActionState hook
93+
import { useActionState } from "react";
94+
95+
// Import the submitForm action
96+
import { submitForm } from "./actions/submitForm";
97+
98+
const Greeter = () => {
99+
100+
// Initialize the hook
101+
const [state, submit, isPending] = useActionState(submitForm, {
102+
message: "",
103+
});
104+
105+
return (
106+
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-6">
107+
{/* Rest of component */}
108+
</div>
109+
);
110+
};
111+
112+
export default Greeter;
113+
```
114+
115+
Here's what the full code looks like with a bit of styling:
116+
117+
```js
118+
"use client";
119+
120+
import { useActionState } from "react";
121+
import { submitForm } from "./actions/submitForm";
122+
123+
const Greeter = () => {
124+
const [state, submit, isPending] = useActionState(submitForm, {
125+
message: "",
126+
});
127+
128+
return (
129+
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100 p-6">
130+
<form
131+
action={submit}
132+
className="bg-white p-6 rounded-2xl shadow-md w-full max-w-md"
133+
>
134+
<h2 className="text-2xl text-center font-semibold text-gray-700 mb-4">
135+
Greet Someone
136+
</h2>
137+
138+
<input
139+
type="text"
140+
name="name"
141+
placeholder="Enter your name"
142+
required
143+
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-400"
144+
/>
145+
146+
<button
147+
type="submit"
148+
disabled={isPending}
149+
className="w-full mt-4 p-3 bg-green-500 text-white font-semibold rounded-lg hover:bg-green-600 disabled:bg-gray-400 transition-all"
150+
>
151+
{isPending ? "Greeting..." : "Greet"}
152+
</button>
153+
154+
{state.message && (
155+
<p className="mt-4 text-green-600 text-center font-medium">
156+
{state.message}
157+
</p>
158+
)}
159+
</form>
160+
</div>
161+
);
162+
};
163+
164+
export default Greeter;
165+
```
166+
167+
In the browser, you would see your form button change from `Greet` to `Greeting...` while the action `isPending` - and the greeting would show `Good morning, {name}`, `Good afternoon, {name}`, or `Good evening, {name}`, depending on what time of day the form was submitted.
168+
169+
Remember how we mentioned that you can also use the `useActionState` hook outside of a form?
170+
171+
In this example, we'll fetch five users from JSONPlaceholder with a button click:
172+
173+
```js
174+
"use server";
175+
176+
export async function getUsers() {
177+
const res = await fetch(
178+
"https://jsonplaceholder.typicode.com/users?_start=0&_limit=5/"
179+
);
180+
return await res.json();
181+
}
182+
```
183+
184+
Here's the styled UI:
185+
186+
```js
187+
"use client";
188+
189+
import { useActionState } from "react";
190+
import { getUsers } from "./actions/getUsers";
191+
192+
export default function FetchUsers() {
193+
const [users, fetchAction, isPending] = useActionState(getUsers, []);
194+
195+
return (
196+
<div className="p-6 max-w-lg mx-auto">
197+
<button
198+
onClick={fetchAction}
199+
disabled={isPending}
200+
className="px-4 py-2 cursor-pointer bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:bg-gray-400 font-bold"
201+
>
202+
{isPending ? "Fetching Users..." : "Fetch Users"}
203+
</button>
204+
205+
<ul className="mt-4 space-y-2">
206+
{users.map((user) => (
207+
<li key={user.id} className="p-3 bg-gray-100 rounded-lg">
208+
<p className="font-semibold">{user.name}</p>
209+
<p className="text-sm text-gray-600">{user.email}</p>
210+
</li>
211+
))}
212+
</ul>
213+
</div>
214+
);
215+
}
216+
```
217+
218+
In the browser, you would see that the button text is never updated to `Fetching Users...` after it's clicked.
219+
220+
This happens because React treats data fetching and rendering as a higher priority than the `isPending` state, which blocks `isPending` in the process and throws an error.
221+
222+
To fix this issue, you need to wrap the action in `startTransition`:
223+
224+
```js
225+
"use client";
226+
227+
import { useActionState } from "react";
228+
import { getUsers } from "./actions/getUsers";
229+
230+
// import startTransition from React
231+
import { startTransition } from "react";
232+
233+
export default function FetchUsers() {
234+
const [users, fetchAction, isPending] = useActionState(getUsers, []);
235+
236+
return (
237+
<div className="p-6 max-w-lg mx-auto">
238+
<button
239+
{/* wrap fetchAction in startTransition */}
240+
onClick={() => startTransition(() => fetchAction())}
241+
disabled={isPending}
242+
className="px-4 py-2 bg-green-500 font-bold cursor-pointer text-white rounded-lg hover:bg-green-600 disabled:bg-gray-400"
243+
>
244+
{isPending ? 'Fetching Users...' : 'Fetch Users'}
245+
</button>
246+
247+
<ul className="mt-4 space-y-2">
248+
{users.map((user) => (
249+
<li key={user.id} className="p-3 bg-gray-100 rounded-lg">
250+
<p className="font-semibold">{user.name}</p>
251+
<p className="text-sm text-gray-600">{user.email}</p>
252+
</li>
253+
))}
254+
</ul>
255+
</div>
256+
);
257+
}
258+
```
259+
260+
If you're wondering what `startTransition` is, it's a function that tells React that a state update is of low-priority and can be interrupted. This keeps the UI responsive while handling asynchronous updates like server actions.
261+
262+
That's how to use the `useActionState` hook inside and outside a form.
12263

13264
# --questions--
14265

0 commit comments

Comments
 (0)