Skip to content
Merged
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
6 changes: 3 additions & 3 deletions docs/framework/react/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ import { z } from 'zod'

## Reactivity

`@tanstack/react-form` offers various ways to subscribe to form and field state changes, most notably the `form.useStore` 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 `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.

Example:

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

Note: The usage of the `form.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 `form.useStore` 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 `useStore(form.store)` instead.

## Listeners

Expand Down
10 changes: 5 additions & 5 deletions docs/framework/react/guides/ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function Home() {
transform: useTransform((baseForm) => mergeForm(baseForm, state), [state]),
})

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

return (
<form action={handleForm.url} method="post" encType={'multipart/form-data'}>
Expand Down Expand Up @@ -249,7 +249,7 @@ export const ClientComp = () => {
transform: useTransform((baseForm) => mergeForm(baseForm, state!), [state]),
})

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

return (
<form action={action as never} onSubmit={() => form.handleSubmit()}>
Expand Down Expand Up @@ -410,8 +410,8 @@ export default function Index() {
[actionData],
),
})
const formErrors = form.useStore((formState) => formState.errors)

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

return (
<Form method="post" onSubmit={() => form.handleSubmit()}>
Expand Down Expand Up @@ -456,4 +456,4 @@ export default function Index() {
}
```

Here, we're using [Remix's `useActionData` hook](https://remix.run/docs/en/main/hooks/use-action-data) and TanStack Form's `useTransform` hook to merge state returned from the server action with the form state.
Here, we're using [Remix's `useActionData` hook](https://remix.run/docs/en/main/hooks/use-action-data) and TanStack Form's `useTransform` hook to merge state returned from the server action with the form state.
2 changes: 1 addition & 1 deletion docs/framework/react/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export default function App() {

// Subscribe to the form's error map so that updates to it will render
// alternately, you can use `form.Subscribe`
const formErrorMap = form.useStore((state) => state.errorMap)
const formErrorMap = useStore(form.store, (state) => state.errorMap)

return (
<div>
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/reference/functions/useform.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ This API encapsulates all the necessary functionalities related to the form. It

## Defined in

[useForm.tsx:54](https://github.com/TanStack/form/blob/main/packages/react-form/src/useForm.tsx#L54)
[useForm.tsx:67](https://github.com/TanStack/form/blob/main/packages/react-form/src/useForm.tsx#L67)
2 changes: 1 addition & 1 deletion docs/reference/classes/formapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ The current state of the form.

**Note:**
Do not use `state` directly, as it is not reactive.
Please use form.useStore() utility to subscribe to state
Please use useStore(form.store) utility to subscribe to state

#### Defined in

Expand Down
2 changes: 1 addition & 1 deletion examples/lit/simple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"lit": "^3.2.1"
},
"devDependencies": {
"vite": "^5.4.10"
"vite": "^5.4.11"
},
"browserslist": {
"production": [
Expand Down
2 changes: 1 addition & 1 deletion examples/lit/ui-libraries/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"lit": "^3.2.1"
},
"devDependencies": {
"vite": "^5.4.10"
"vite": "^5.4.11"
},
"browserslist": {
"production": [
Expand Down
2 changes: 1 addition & 1 deletion examples/react/array/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.3",
"vite": "^5.4.10"
"vite": "^5.4.11"
},
"browserslist": {
"production": [
Expand Down
16 changes: 16 additions & 0 deletions examples/react/compiler/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @ts-check
const reactCompiler = require('eslint-plugin-react-compiler')

/** @type {import('eslint').Linter.Config} */
const config = {
plugins: {
'react-compiler': reactCompiler,
},
extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
rules: {
'react/no-children-prop': 'off',
'react-compiler/react-compiler': 'error',
},
}

module.exports = config
27 changes: 27 additions & 0 deletions examples/react/compiler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

pnpm-lock.yaml
yarn.lock
package-lock.json

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
6 changes: 6 additions & 0 deletions examples/react/compiler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install`
- `npm run dev`
16 changes: 16 additions & 0 deletions examples/react/compiler/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/emblem-light.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />

<title>TanStack Form React Simple Example App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions examples/react/compiler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@tanstack/form-example-react-compiler",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port=3001",
"build": "vite build",
"preview": "vite preview",
"_test:types": "tsc"
},
"dependencies": {
"@tanstack/react-form": "^0.38.0",
"react": "^19.0.0-rc.1",
"react-dom": "^19.0.0-rc.1"
},
"devDependencies": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc",
"@vitejs/plugin-react": "^4.3.3",
"babel-plugin-react-compiler": "^19.0.0-beta-0dec889-20241115",
"eslint-plugin-react-compiler": "^19.0.0-beta-0dec889-20241115",
"vite": "^5.4.11"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
13 changes: 13 additions & 0 deletions examples/react/compiler/public/emblem-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 118 additions & 0 deletions examples/react/compiler/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { useForm } from '@tanstack/react-form'
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import type { FieldApi } from '@tanstack/react-form'

function FieldInfo({ field }: { field: FieldApi<any, any, any, any> }) {
return (
<>
{field.state.meta.isTouched && field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(',')}</em>
) : null}
{field.state.meta.isValidating ? 'Validating...' : null}
</>
)
}

export default function App() {
const form = useForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit: async ({ value }) => {
// Do something with form data
console.log(value)
},
})

return (
<div>
<h1>Simple Form Example</h1>
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<div>
{/* A type-safe field component*/}
<form.Field
name="firstName"
validators={{
onChange: ({ value }) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined,
onChangeAsyncDebounceMs: 500,
onChangeAsync: async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return (
value.includes('error') && 'No "error" allowed in first name'
)
},
}}
children={(field) => {
// Avoid hasty abstractions. Render props are great!
return (
<>
<label htmlFor={field.name}>First Name:</label>
<input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
<FieldInfo field={field} />
</>
)
}}
/>
</div>
<div>
<form.Field
name="lastName"
children={(field) => (
<>
<label htmlFor={field.name}>Last Name:</label>
<input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
<FieldInfo field={field} />
</>
)}
/>
</div>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<>
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? '...' : 'Submit'}
</button>
<button type="reset" onClick={() => form.reset()}>
Reset
</button>
</>
)}
/>
</form>
</div>
)
}

const rootElement = document.getElementById('root')!

createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
23 changes: 23 additions & 0 deletions examples/react/compiler/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
Loading