Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Since v0.0.15, FieldApi reflects Field values but not FormApi #437

Closed
Toru-Takagi opened this issue Sep 3, 2023 · 9 comments · Fixed by #440
Closed

Since v0.0.15, FieldApi reflects Field values but not FormApi #437

Toru-Takagi opened this issue Sep 3, 2023 · 9 comments · Fixed by #440
Assignees
Labels

Comments

@Toru-Takagi
Copy link

Describe the bug

This is the issue we have reported here.
#430 (comment)

console.log(field)

※A Compound

FieldApi
  form: {
    state: {
      values: {
        label: "test label"
      }
    },
    store: {
      state: {
        values: {
          label: "test label"
        }
      }
    }
  },
  optons: {
    form: {
      state: {
        values: {
          label: "test label"
        }
      },
      store: {
        state: {
          values: {
            label: "test label"
          }
        }
      }
    }
  },
  prevState: {
    meta: {
      isTouched: true,
      isValidating: false,
      touchedError: undefined
    }
    value: "test label"
  },
  state: {
    meta: {
      isTouched: true,
      isValidating: false,
      touchedError: undefined
    }
    value: "test label"
  }

console.log(store)

※A Compound

Store
  state: {
    values: {
      label: ''
    }
  }

console.log(state)

※A Compound

{
  values: {}
}

console.log(values)

{
  label: ''
}

Simple code that occurs with the use of Next.

import { useForm } from '@tanstack/react-form'
import { NextPage } from 'next'
import { useMemo } from 'react'

const FormTest: NextPage = () => {
  const { Provider, Field, Subscribe, getFormProps, state, store } = useForm({
    defaultValues: useMemo(() => {
      return {
        label: '',
      }
    }, []),
    onSubmit: async (values) => {
      console.log(store)
      console.log(state)
      console.log(values)
    },
  })

  return (
    <Provider>
      <form {...getFormProps()}>
        <Field name='label'>
          {(field) => {
            console.log(field)
            return <input type='text' name={field.name} {...field.getInputProps()} />
          }}
        </Field>
        <Subscribe>
          {({ canSubmit }) => (
            <button type='submit' disabled={!canSubmit}>
              Submit
            </button>
          )}
        </Subscribe>
      </form>
    </Provider>
  )
}

export default FormTest

Your minimal, reproducible example

Described in the description

Steps to reproduce

  1. Enter text in the label field
  2. Click the Submit button
  3. Check the console

Expected behavior

The values argument of onSubmit should reflect what was entered into the Field.

How often does this bug happen?

None

Screenshots or Videos

No response

Platform

  • OS: Mac
  • Node: v18.17.1
  • @tanstack/react-form: ^0.0.15
  • next: 13.4.16
  • react: 18.2.0
  • typescript: ^5.1.6

Tanstack Form adapter

None

TanStack Form version

v0.0.15

TypeScript version

v5.1.6

Additional context

No response

@crutchcorn
Copy link
Member

crutchcorn commented Sep 3, 2023

I'm reasonably certain this isn't really a bug with TanStack Form 😅

What happens if you do not use object destructuring on useForm? It's not intended to be used in this way and may cause state and store to go out of sync

@crutchcorn
Copy link
Member

Hmm no wait nevermind. There's absolutely something wrong with submit even when not binding {}. Let me investigate

@crutchcorn crutchcorn reopened this Sep 3, 2023
@crutchcorn crutchcorn added the bug label Sep 3, 2023
@crutchcorn crutchcorn self-assigned this Sep 3, 2023
@Toru-Takagi
Copy link
Author

I've come to realize that useForm does not support destructuring. That's my oversight, and I apologize.

I've treated it as an object now, but it doesn't seem to be reflected in onSubmit.

I apologize for the inconvenience this may have caused. Thank you for your continued support.

If you need any more information, please feel free to comment, and I will provide it.

import { useForm } from '@tanstack/react-form'
import { NextPage } from 'next'
import { useMemo } from 'react'

const FormTest: NextPage = () => {
  const form = useForm({
    defaultValues: useMemo(() => {
      return {
        label: '',
      }
    }, []),
    onSubmit: async (values) => {
      console.log(values)
    },
  })

  return (
    <form.Provider>
      <form {...form.getFormProps()}>
        <form.Field name='label'>
          {(field) => {
            console.log(field)
            return <input {...field.getInputProps()} type='text' name={field.name} />
          }}
        </form.Field>
        <form.Subscribe>
          {({ canSubmit }) => (
            <button type='submit' disabled={!canSubmit}>
              Submit
            </button>
          )}
        </form.Subscribe>
      </form>
    </form.Provider>
  )
}

export default FormTest

@crutchcorn
Copy link
Member

No inconvenience - I appreciate you filing this! :)

I think I've already identified this issue:

https://github.com/TanStack/form/blob/main/packages/react-form/src/useForm.tsx#L79-L81

This triggers every time due to opts being passed by useForm not being stable.

The temporary workaround is to wrap your useForm props in useMemo like so:

  const form = useForm(useMemo(() => ({
    defaultValues: {
      return {
        label: '',
      }
    },
    onSubmit: async (values) => {
      console.log(values)
    },
  }), [])

@Toru-Takagi
Copy link
Author

Thank you so much!!!
I've confirmed that it's working as intended by using a temporary workaround.

I appreciate your quick identification of the issue.

import { useForm } from '@tanstack/react-form'
import { NextPage } from 'next'
import { useMemo } from 'react'

const FormTest: NextPage = () => {
  const form = useForm<{ label: string }>(
    useMemo(
      () => ({
        defaultValues: { label: '' },
        onSubmit: async (values) => {
          console.log(values)
        },
      }),
      []
    )
  )

  return (
    <form.Provider>
      <form {...form.getFormProps()}>
        <form.Field name='label'>
          {(field) => {
            console.log(field)
            return <input {...field.getInputProps()} type='text' name={field.name} />
          }}
        </form.Field>
        <form.Subscribe>
          {({ canSubmit }) => (
            <button type='submit' disabled={!canSubmit}>
              Submit
            </button>
          )}
        </form.Subscribe>
      </form>
    </form.Provider>
  )
}

export default FormTest

@crutchcorn crutchcorn mentioned this issue Sep 4, 2023
3 tasks
@crutchcorn
Copy link
Member

@Toru-Takagi we have a potential long-term fix in PR #440, but it has some API changes that are rather unorthodox. I want to get some feedback from the rest of the team before moving forward with merging it. Expect to hear more news Tuesday or Wednesday

@Toru-Takagi
Copy link
Author

Thank you very much for your prompt response.
We look forward to your correction.

@crutchcorn
Copy link
Member

crutchcorn commented Sep 7, 2023

Here's the good news @Toru-Takagi: this should now be fixed in 0.1.2

Here's the better news: No more useMemo in useForm. No more useCallback. Just a normal object should do fine 😄

Please let us know if you run into any further issues and thank you for reporting!

@Toru-Takagi
Copy link
Author

Thanks for the fix 🙇

Here's the better news: No more useMemo in useForm. No more useCallback. Just a normal object should do fine 😄

This is very good news.
It's simplified and wonderful!

Please let us know if you run into any further issues and thank you for reporting!

Glad to be of service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants