Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions content/en/guide/v10/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const App = <MyComponent name="John Doe" />;
render(App, document.body);
```

Functional components often use Hooks to manage state, but Preact also provides [Signals](/guide/v10/signals). Signals are a reactive state primitive that can be used **inside or outside** components, offering fine-grained updates and a reactivity model many users find to be simpler. They're a great option for managing state in modern Preact apps.

> Note in earlier versions they were known as `"Stateless Components"`. This doesn't hold true anymore with the [hooks-addon](/guide/v10/hooks).

## Class Components
Expand Down
147 changes: 142 additions & 5 deletions content/en/guide/v10/forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Often you'll want to collect user input in your application, and this is where `

To get started, we'll create a simple text input field that will update a state value as the user types. We'll use the `onInput` event to listen for changes to the input field's value and update the state per-keystroke. This state value is then rendered in a `<p>` element, so we can see the results.

<tab-group tabstring="Classes, Hooks">
<tab-group tabstring="Classes, Hooks, Signals">

```jsx
// --repl
Expand Down Expand Up @@ -68,11 +68,32 @@ function BasicInput() {
render(<BasicInput />, document.getElementById('app'));
```

```jsx
// --repl
import { render } from 'preact';
import { useSignal } from '@preact/signals';
// --repl-before
function BasicInput() {
const name = useSignal('');

return (
<div class="form-example">
<label>
Name: <input onInput={e => (name.value = e.currentTarget.value)} />
</label>
<p>Hello {name.value}</p>
</div>
);
}
// --repl-after
render(<BasicInput />, document.getElementById('app'));
```

</tab-group>

### Input (checkbox & radio)

<tab-group tabstring="Classes, Hooks">
<tab-group tabstring="Classes, Hooks, Signals">

```jsx
// --repl
Expand Down Expand Up @@ -194,11 +215,69 @@ function BasicRadioButton() {
render(<BasicRadioButton />, document.getElementById('app'));
```

```jsx
// --repl
import { render } from 'preact';
import { useSignal } from '@preact/signals';
// --repl-before
function BasicRadioButton() {
const allowContact = useSignal(false);
const contactMethod = useSignal('');

const toggleContact = () => (allowContact.value = !allowContact.value);
const setRadioValue = e => (contactMethod.value = e.currentTarget.value);

return (
<div class="form-example">
<label>
Allow contact: <input type="checkbox" onClick={toggleContact} />
</label>
<label>
Phone:{' '}
<input
type="radio"
name="contact"
value="phone"
onClick={setRadioValue}
disabled={!allowContact.value}
/>
</label>
<label>
Email:{' '}
<input
type="radio"
name="contact"
value="email"
onClick={setRadioValue}
disabled={!allowContact.value}
/>
</label>
<label>
Mail:{' '}
<input
type="radio"
name="contact"
value="mail"
onClick={setRadioValue}
disabled={!allowContact.value}
/>
</label>
<p>
You {allowContact.value ? 'have allowed' : 'have not allowed'} contact{' '}
{allowContact.value && ` via ${contactMethod.value}`}
</p>
</div>
);
}
// --repl-after
render(<BasicRadioButton />, document.getElementById('app'));
```

</tab-group>

### Select

<tab-group tabstring="Classes, Hooks">
<tab-group tabstring="Classes, Hooks, Signals">

```jsx
// --repl
Expand Down Expand Up @@ -244,7 +323,30 @@ function MySelect() {
<option value="C">C</option>
</select>
<p>You selected: {value}</p>
</form>
</div>
);
}
// --repl-after
render(<MySelect />, document.getElementById('app'));
```

```jsx
// --repl
import { render } from 'preact';
import { useSignal } from '@preact/signals';
// --repl-before
function MySelect() {
const value = useSignal('');

return (
<div class="form-example">
<select onChange={e => (value.value = e.currentTarget.value)}>
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
<p>You selected: {value.value}</p>
</div>
);
}
// --repl-after
Expand All @@ -259,7 +361,7 @@ Whilst bare inputs are useful and you can get far with them, often we'll see our

To demonstrate, we'll create a new `<form>` element that contains two `<input>` fields: one for a user's first name and one for their last name. We'll use the `onSubmit` event to listen for the form submission and update the state with the user's full name.

<tab-group tabstring="Classes, Hooks">
<tab-group tabstring="Classes, Hooks, Signals">

```jsx
// --repl
Expand Down Expand Up @@ -333,6 +435,41 @@ function FullNameForm() {
render(<FullNameForm />, document.getElementById('app'));
```

```jsx
// --repl
import { render } from 'preact';
import { useSignal } from '@preact/signals';
// --repl-before
function FullNameForm() {
const fullName = useSignal('');

const onSubmit = e => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
fullName.value = formData.get('firstName') + ' ' + formData.get('lastName');
e.currentTarget.reset(); // Clear the inputs to prepare for the next submission
};

return (
<div class="form-example">
<form onSubmit={onSubmit}>
<label>
First Name: <input name="firstName" />
</label>
<label>
Last Name: <input name="lastName" />
</label>
<button>Submit</button>
</form>
{fullName.value && <p>Hello {fullName.value}</p>}
</div>
);
}

// --repl-after
render(<FullNameForm />, document.getElementById('app'));
```

</tab-group>

> **Note**: Whilst it's quite common to see React & Preact forms that link every input field to component state, it's often unnecessary and can get unwieldy. As a very loose rule of thumb, you should prefer using `onSubmit` and the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) API in most cases, using component state only when you need to. This reduces the complexity of your components and may skip unnecessary rerenders.
Expand Down
2 changes: 1 addition & 1 deletion content/en/tutorial/09-error-handling.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Error Handling
prev: /tutorial/08-keys
next: /tutorial/10-links
next: /tutorial/10-signals
solvable: true
---

Expand Down
Loading