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
9 changes: 9 additions & 0 deletions .changeset/use-selector-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@tanstack/react-form': patch
'@tanstack/preact-form': patch
'@tanstack/vue-form': patch
'@tanstack/solid-form': patch
'@tanstack/svelte-form': patch
---

Re-export `useSelector` from TanStack Store adapters and document migration from deprecated `useStore` (fixes #2203).
16 changes: 9 additions & 7 deletions docs/framework/preact/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,14 @@ function App() {

## Reactivity

`@tanstack/preact-form` offers various ways to subscribe to form and field state changes, most notably the `useStore(form.store)` hook and the `form.Subscribe` component. These methods allow you to optimize your form's rendering performance by only updating components when necessary.
`@tanstack/preact-form` offers various ways to subscribe to form and field state changes, most notably the `useSelector(form.store, …)` hook and the `form.Subscribe` component. These methods allow you to optimize your form's rendering performance by only updating components when necessary.

Example:

```tsx
const firstName = useStore(form.store, (state) => state.values.firstName)
import { useSelector } from '@tanstack/preact-form'

const firstName = useSelector(form.store, (state) => state.values.firstName)
//...
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
Expand All @@ -253,17 +255,17 @@ const firstName = useStore(form.store, (state) => state.values.firstName)
/>
```

It is important to remember that while the `useStore` hook's `selector` prop is optional, it is strongly recommended to provide one, as omitting it will result in unnecessary re-renders.
It is important to remember that while the `useSelector` hook's `selector` prop is optional, it is strongly recommended to provide one, as omitting it will result in unnecessary re-renders.

```tsx
// Correct use
const firstName = useStore(form.store, (state) => state.values.firstName)
const errors = useStore(form.store, (state) => state.errorMap)
const firstName = useSelector(form.store, (state) => state.values.firstName)
const errors = useSelector(form.store, (state) => state.errorMap)
// Incorrect use
const store = useStore(form.store)
const store = useSelector(form.store)
```

Note: The usage of the `useField` hook to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `useStore(form.store)` instead.
Note: The usage of the `useField` hook to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `useSelector(form.store, …)` instead.

## Listeners

Expand Down
4 changes: 2 additions & 2 deletions docs/framework/preact/guides/form-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,9 @@ const FieldGroupPasswordFields = withFieldGroup({
// Internally, you will have access to a `group` instead of a `form`
render: function Render({ group, title }) {
// access reactive values using the group store
const password = useStore(group.store, (state) => state.values.password)
const password = useSelector(group.store, (state) => state.values.password)
// or the form itself
const isSubmitting = useStore(
const isSubmitting = useSelector(
group.form.store,
(state) => state.isSubmitting,
)
Expand Down
20 changes: 9 additions & 11 deletions docs/framework/preact/guides/reactivity.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ title: Reactivity

Tanstack Form doesn't cause re-renders when interacting with the form. So, you might find yourself trying to use a form or field state value without success.

If you would like to access reactive values, you will need to subscribe to them using one of two methods: `useStore` or the `form.Subscribe` component.
If you would like to access reactive values, you will need to subscribe to them using one of two methods: `useSelector` or the `form.Subscribe` component.

Some uses for these subscriptions are rendering up-to-date field values, determining what to render based on a condition, or using field values inside the logic of your component.

> For situations where you want to "react" to triggers, check out the [listener](./listeners.md) API.

## useStore
## useSelector

The `useStore` hook is perfect when you need to access form values within the logic of your component. `useStore` takes two parameters. First, the form store. Second, a selector to specify the piece of the form you wish to subscribe to.
Import `useSelector` from `@tanstack/preact-form` when you need form values inside component logic.

```tsx
const firstName = useStore(form.store, (state) => state.values.firstName)
const errors = useStore(form.store, (state) => state.errorMap)
```

You can access any piece of the form state in the selector.
import { useSelector } from '@tanstack/preact-form'

> Note, that `useStore` will cause a whole component re-render whenever the value subscribed to changes.
const firstName = useSelector(form.store, (state) => state.values.firstName)
const errors = useSelector(form.store, (state) => state.errorMap)
```

While it IS possible to omit the selector, resist the urge as omitting it would result in many unnecessary re-renders whenever any of the form state changes.
> **Migration:** `useStore` is still exported but deprecated; use `useSelector` with the same arguments.

## form.Subscribe

Expand All @@ -49,4 +47,4 @@ The `form.Subscribe` component is best suited when you need to react to somethin

> The `form.Subscribe` component doesn't trigger component-level re-renders. Anytime the value subscribed to changes, only the `form.Subscribe` component re-renders.

The choice between whether to use `useStore` or `form.Subscribe` mainly boils down to your use case. If you're aiming for direct UI updates based on form state, use `form.Subscribe` for its optimization perks. And if you need the reactivity within the logic, then `useStore` is the better choice.
The choice between whether to use `useSelector` or `form.Subscribe` mainly boils down to your use case. If you're aiming for direct UI updates based on form state, use `form.Subscribe` for its optimization perks. And if you need the reactivity within the logic, then `useSelector` is the better choice.
2 changes: 1 addition & 1 deletion docs/framework/preact/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export default function App() {

// Subscribe to the form's `errorMap` so that updates to it will cause re-renders
// Alternatively, you can use `form.Subscribe`
const formErrorMap = useStore(form.store, (state) => state.errorMap)
const formErrorMap = useSelector(form.store, (state) => state.errorMap)

return (
<div>
Expand Down
1 change: 1 addition & 0 deletions docs/framework/preact/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ title: "@tanstack/preact-form"
- [Field](variables/Field.md)
- [FormGroup](variables/FormGroup.md)
- [useIsomorphicLayoutEffect](variables/useIsomorphicLayoutEffect.md)
- [useSelector](variables/useSelector.md)
- [~~useStore~~](variables/useStore.md)

## Functions
Expand Down
25 changes: 25 additions & 0 deletions docs/framework/preact/reference/variables/useSelector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: useSelector
title: useSelector
---

# Variable: useSelector()

```ts
const useSelector: <TSource, TSelected>(source, selector?, options?) => TSelected;
```

Re-exported from `@tanstack/preact-store`. Use this hook to subscribe to slices of `form.store` inside component logic.

## Example

```tsx
import { useForm, useSelector } from '@tanstack/preact-form'

const form = useForm({ /* ... */ })
const firstName = useSelector(form.store, (state) => state.values.firstName)
```

## Migration from useStore

`useStore` is deprecated; import `useSelector` from `@tanstack/preact-form` instead. The API is the same aside from optional `options.compare` instead of a bare `compare` third argument.
6 changes: 5 additions & 1 deletion docs/framework/preact/reference/variables/useStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ const count = useStore(counterStore, (state) => state.count)

## Deprecated

Use `useSelector` instead.
Use [`useSelector`](useSelector.md) instead. You can import it from `@tanstack/preact-form`:

```tsx
import { useSelector } from '@tanstack/preact-form'
```
18 changes: 11 additions & 7 deletions docs/framework/react/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,14 @@ function App() {

## Reactivity

`@tanstack/react-form` offers various ways to subscribe to form and field state changes, most notably the `useStore(form.store)` hook and the `form.Subscribe` component. These methods allow you to optimize your form's rendering performance by only updating components when necessary.
`@tanstack/react-form` offers various ways to subscribe to form and field state changes, most notably the `useSelector(form.store, …)` hook and the `form.Subscribe` component. These methods allow you to optimize your form's rendering performance by only updating components when necessary.

Example:

```tsx
const firstName = useStore(form.store, (state) => state.values.firstName)
import { useSelector } from '@tanstack/react-form'

const firstName = useSelector(form.store, (state) => state.values.firstName)
//...
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
Expand All @@ -253,17 +255,19 @@ const firstName = useStore(form.store, (state) => state.values.firstName)
/>
```

It is important to remember that while the `useStore` hook's `selector` prop is optional, it is strongly recommended to provide one, as omitting it will result in unnecessary re-renders.
It is important to remember that while the `useSelector` hook's selector argument is optional, it is strongly recommended to provide one, as omitting it will result in unnecessary re-renders.

```tsx
import { useSelector } from '@tanstack/react-form'

// Correct use
const firstName = useStore(form.store, (state) => state.values.firstName)
const errors = useStore(form.store, (state) => state.errorMap)
const firstName = useSelector(form.store, (state) => state.values.firstName)
const errors = useSelector(form.store, (state) => state.errorMap)
// Incorrect use
const store = useStore(form.store)
const store = useSelector(form.store)
```

Note: The usage of the `useField` hook to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `useStore(form.store)` instead.
Note: The usage of the `useField` hook to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `useSelector(form.store, …)` instead.

## Listeners

Expand Down
4 changes: 2 additions & 2 deletions docs/framework/react/guides/form-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,9 @@ const FieldGroupPasswordFields = withFieldGroup({
// Internally, you will have access to a `group` instead of a `form`
render: function Render({ group, title }) {
// access reactive values using the group store
const password = useStore(group.store, (state) => state.values.password)
const password = useSelector(group.store, (state) => state.values.password)
// or the form itself
const isSubmitting = useStore(
const isSubmitting = useSelector(
group.form.store,
(state) => state.isSubmitting,
)
Expand Down
18 changes: 11 additions & 7 deletions docs/framework/react/guides/reactivity.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ title: Reactivity

Tanstack Form doesn't cause re-renders when interacting with the form. So, you might find yourself trying to use a form or field state value without success.

If you would like to access reactive values, you will need to subscribe to them using one of two methods: `useStore` or the `form.Subscribe` component.
If you would like to access reactive values, you will need to subscribe to them using one of two methods: `useSelector` or the `form.Subscribe` component.

Some uses for these subscriptions are rendering up-to-date field values, determining what to render based on a condition, or using field values inside the logic of your component.

> For situations where you want to "react" to triggers, check out the [listener](./listeners.md) API.

## useStore
## useSelector

The `useStore` hook is perfect when you need to access form values within the logic of your component. `useStore` takes two parameters. First, the form store. Second, a selector to specify the piece of the form you wish to subscribe to.
The `useSelector` hook is perfect when you need to access form values within the logic of your component. Import it from `@tanstack/react-form`. It takes the form store as its first argument and a selector as its second.

```tsx
const firstName = useStore(form.store, (state) => state.values.firstName)
const errors = useStore(form.store, (state) => state.errorMap)
import { useSelector } from '@tanstack/react-form'

const firstName = useSelector(form.store, (state) => state.values.firstName)
const errors = useSelector(form.store, (state) => state.errorMap)
```

You can access any piece of the form state in the selector.

> Note, that `useStore` will cause a whole component re-render whenever the value subscribed to changes.
> Note, that `useSelector` will cause a whole component re-render whenever the value subscribed to changes.

While it IS possible to omit the selector, resist the urge as omitting it would result in many unnecessary re-renders whenever any of the form state changes.

> **Migration:** `useStore` is still exported but deprecated (it is an alias from `@tanstack/react-store`). Replace `useStore` with `useSelector` — same arguments, or pass `{ compare }` as the third argument instead of a bare `compare` function.

## form.Subscribe

The `form.Subscribe` component is best suited when you need to react to something within the UI of your component. For example, showing or hiding UI based on the value of a form field.
Expand All @@ -49,4 +53,4 @@ The `form.Subscribe` component is best suited when you need to react to somethin

> The `form.Subscribe` component doesn't trigger component-level re-renders. Anytime the value subscribed to changes, only the `form.Subscribe` component re-renders.

The choice between whether to use `useStore` or `form.Subscribe` mainly boils down to your use case. If you're aiming for direct UI updates based on form state, use `form.Subscribe` for its optimization perks. And if you need the reactivity within the logic, then `useStore` is the better choice.
The choice between whether to use `useSelector` or `form.Subscribe` mainly boils down to your use case. If you're aiming for direct UI updates based on form state, use `form.Subscribe` for its optimization perks. And if you need the reactivity within the logic, then `useSelector` is the better choice.
12 changes: 6 additions & 6 deletions docs/framework/react/guides/ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ import { createFileRoute } from '@tanstack/react-router'
import {
mergeForm,
useForm,
useStore,
useSelector,
useTransform,
} from '@tanstack/react-form-start'

Expand All @@ -128,7 +128,7 @@ function Home() {
transform: useTransform((baseForm) => mergeForm(baseForm, state), [state]),
})

const formErrors = useStore(form.store, (formState) => formState.errors)
const formErrors = useSelector(form.store, (formState) => formState.errors)

return (
<form action={handleForm.url} method="post" encType={'multipart/form-data'}>
Expand Down Expand Up @@ -259,7 +259,7 @@ import {
initialFormState,
mergeForm,
useForm,
useStore,
useSelector,
useTransform,
} from '@tanstack/react-form-nextjs'
import someAction from './action'
Expand All @@ -273,7 +273,7 @@ export const ClientComp = () => {
transform: useTransform((baseForm) => mergeForm(baseForm, state!), [state]),
})

const formErrors = useStore(form.store, (formState) => formState.errors)
const formErrors = useSelector(form.store, (formState) => formState.errors)

return (
<form action={action as never} onSubmit={() => form.handleSubmit()}>
Expand Down Expand Up @@ -414,7 +414,7 @@ import {
mergeForm,
useActionData,
useForm,
useStore,
useSelector,
useTransform,
} from '@tanstack/react-form'
import {
Expand Down Expand Up @@ -443,7 +443,7 @@ export default function Index() {
),
})

const formErrors = useStore(form.store, (formState) => formState.errors)
const formErrors = useSelector(form.store, (formState) => formState.errors)

return (
<Form method="post" onSubmit={() => form.handleSubmit()}>
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export default function App() {

// Subscribe to the form's `errorMap` so that updates to it will cause re-renders
// Alternatively, you can use `form.Subscribe`
const formErrorMap = useStore(form.store, (state) => state.errorMap)
const formErrorMap = useSelector(form.store, (state) => state.errorMap)

return (
<div>
Expand Down
1 change: 1 addition & 0 deletions docs/framework/react/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ title: "@tanstack/react-form"
- [Field](variables/Field.md)
- [FormGroup](variables/FormGroup.md)
- [useIsomorphicLayoutEffect](variables/useIsomorphicLayoutEffect.md)
- [useSelector](variables/useSelector.md)
- [~~useStore~~](variables/useStore.md)

## Functions
Expand Down
53 changes: 53 additions & 0 deletions docs/framework/react/reference/variables/useSelector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
id: useSelector
title: useSelector
---

# Variable: useSelector()

```ts
const useSelector: <TSource, TSelected>(source, selector?, options?) => TSelected;
```

Re-exported from `@tanstack/react-store`. Use this hook to subscribe to slices of `form.store` (or any TanStack Store source) inside component logic.

## Parameters

### source

A store or atom with `get()` and `subscribe()`.

### selector?

`(snapshot) => TSelected` — strongly recommended; omitting it subscribes to the entire store and may cause extra re-renders.

### options?

- **compare?** — `(a, b) => boolean` custom equality check (defaults to `===`).

## Returns

`TSelected`

## Example

```tsx
import { useForm, useSelector } from '@tanstack/react-form'

const form = useForm({ /* ... */ })
const firstName = useSelector(form.store, (state) => state.values.firstName)
```

## Migration from useStore

`useStore` is a deprecated alias of `useSelector` with the same signature (the third argument is `compare` on `useStore`, or `options.compare` on `useSelector`):

```tsx
// Before
import { useStore } from '@tanstack/react-form'
const firstName = useStore(form.store, (state) => state.values.firstName)

// After
import { useSelector } from '@tanstack/react-form'
const firstName = useSelector(form.store, (state) => state.values.firstName)
```
6 changes: 5 additions & 1 deletion docs/framework/react/reference/variables/useStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ const count = useStore(counterStore, (state) => state.count)

## Deprecated

Use `useSelector` instead.
Use [`useSelector`](useSelector.md) instead. You can import it from `@tanstack/react-form`:

```tsx
import { useSelector } from '@tanstack/react-form'
```
Loading