Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
161 changes: 156 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}</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,80 @@ function BasicRadioButton() {
render(<BasicRadioButton />, document.getElementById('app'));
```

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

const setRadioValue = e => (contactMethod.value = e.currentTarget.value);
const isDisabled = useComputed(() => !allowContact.value);
const contactStatus = useComputed(
() => `have allowed via ${contactMethod.value}`
);

return (
<div class="form-example">
<label>
Allow contact:
<input
type="checkbox"
checked={allowContact.value}
onChange={e => (allowContact.value = e.target.checked)}
/>
</label>
<label>
Phone:{' '}
<input
type="radio"
name="contact"
value="phone"
onClick={setRadioValue}
disabled={isDisabled}
/>
</label>
<label>
Email:{' '}
<input
type="radio"
name="contact"
value="email"
onClick={setRadioValue}
disabled={isDisabled}
/>
</label>
<label>
Mail:{' '}
<input
type="radio"
name="contact"
value="mail"
onClick={setRadioValue}
disabled={isDisabled}
/>
</label>
<p>
You{' '}
<Show when={allowContact} fallback="have not allowed">
{() => contactStatus}
</Show>
</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 +334,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}</p>
</div>
);
}
// --repl-after
Expand All @@ -259,7 +372,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 +446,44 @@ function FullNameForm() {
render(<FullNameForm />, document.getElementById('app'));
```

```jsx
// --repl
import { render } from 'preact';
import { useSignal, useComputed } from '@preact/signals';
import { Show } from '@preact/signals/utils';
// --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
};

const greeting = useComputed(() => `Hello ${fullName.value}`);

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>
<Show when={fullName}>{() => <p>{greeting}</p>}</Show>
</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