Skip to content

Releases: seasonedcc/remix-forms

v3.0.0

23 Jan 21:15
Compare
Choose a tag to compare

Breaking changes

1. Only React Router v7 is supported

Remix Forms v3 removed support for Remix and React Router v6. Before upgrading Remix Forms, please upgrade your app to React Router v7.

The Remix team made upgrading easy, so you probably should. If you can't upgrade, Remix Forms 2.3.0 is stable and will continue working with React Router v6 and Remix.

2. createForm replaced by SchemaForm

Instead of using createForm, you can now import SchemaForm directly from remix-forms.

Before:

import { createForm } from 'remix-forms'
// For Remix, import it like this
import { Form as FrameworkForm, useActionData, useSubmit, useNavigation } from '@remix-run/react'
// For React Router 6.4, like this
import { Form as FrameworkForm, useActionData, useSubmit, useNavigation } from 'react-router-dom'

const Form = createForm({ component: FrameworkForm, useNavigation, useSubmit, useActionData })

export { Form }

After:

import { SchemaForm } from 'remix-forms'

We stopped using the name Form for our form component to avoid ambiguity with React Router's Form component. We now refer to our forms as "schema forms".

3. createFormAction replaced by formAction

Instead of using createFormAction, you can now import formAction directly from remix-forms.

Before:

import { createFormAction } from 'remix-forms'
// For Remix, import it like this
import { redirect, json } from '@remix-run/node'
// For React Router 6.4, like this
import { redirect, json } from 'react-router-dom'

const formAction = createFormAction({ redirect, json })

export { formAction }

After:

import { formAction } from 'remix-forms'

4. domain-functions replaced by composable-functions

We now depend on Composable Functions for our mutations. If you are a heavy Domain Functions user, please follow our complete migration guide. For most Remix Forms users, only a few simple changes are needed.

makeDomainFunction becomes applySchema

If you already have mutations created with makeDomainFunction or mdf, use applySchema instead.

Before:

import { makeDomainFunction } from 'domain-functions'

const mutation = makeDomainFunction(schema)(async (values) => (
  console.log(values) /* or anything else, like saveMyValues(values) */
))

After:

import { applySchema } from 'composable-functions'

const mutation = applySchema(schema)(async (values) => (
  console.log(values) /* or anything else, like saveMyValues(values) */
))

environment becomes context

Composable Functions renamed environment to context.

Before:

export const action: ActionFunction = async ({ request }) => {
  return formAction({
    request,
    schema,
    mutation,
    environment: { customHeader: request.headers.get('customHeader') },
  })
}

After:

export const action = async ({ request }: Route.ActionArgs) => {
  return formAction({
    request,
    schema,
    mutation,
    context: { customHeader: request.headers.get('customHeader') },
  })
}

Global errors with Error

Composable Functions requires us to throw an instance of Error for generating global errors. We cannot throw literal strings anymore.

Before:

const mutation = makeDomainFunction(schema)(async (values) => {
  if (values.password !== 'supersafe') {
    throw 'Wrong email or password'
  }

  return values
})

After:

const mutation = applySchema(schema)(async (values) => {
  if (values.password !== 'supersafe') {
    throw new Error('Wrong email or password')
  }

  return values
})

The second param to InputError is now an array

We now need to pass the field name as an array when throwing field errors.

Before:

const mutation = makeDomainFunction(schema)(async (values) => {
  if (takenEmails.includes(values.email)) {
    throw new InputError('Email already taken', 'email')
  }

  return values
})

After:

const mutation = applySchema(schema)(async (values) => {
  if (takenEmails.includes(values.email)) {
    throw new InputError('Email already taken', ['email'])
  }

  return values
})

5. PerformMutation type renamed to MutationResult

If you are using the PerformMutation type, you'll need to change to MutationResult from now on. The type is the same, only the name has changed.

6. formAction always returns the mutation result

Now we always return the full MutationResult on formAction. If you use formAction without a successPath and manually get the action data in your components, you'll have to check for success first and only then access the mutation result inside data:

export const action = async ({ request }: Route.ActionArgs) =>
  formAction({ request, schema, mutation })

export default function Component({ actionData }: Route.ComponentProps) {
  if (!actionData) {
    return <p>Our action has not run yet.</p>
  }

  if (!actionData.success) {
    return (
      <div>
        <h3>Errors:</h3>
        <pre>{JSON.stringify(actionData.errors, null, 2)}</pre>
        <h3>Values:</h3>
        <pre>{JSON.stringify(actionData.values, null, 2)}</pre>
      </div>
    )
  }

  return (
    <div>
      <h3>Data:</h3>
      <pre>{JSON.stringify(actionData.data, null, 2)}</pre>
    </div>
  )
}

7. formAction returns 422 status on failed mutations

To avoid revalidating loaders unnecessarily, formAction now returns a 422 status on failed mutations.

8. Removed beforeAction and beforeSuccess callbacks

We removed the beforeAction and beforeSuccess callbacks from formAction in favor of transformedResult, described below.

9. Removed Callback type

Because we don't have callbacks anymore, we removed the Callback type that was previously exported.

10. Removed deprecated parseActionData

We removed parseActionData from our schema form component, which was marked as deprecated since v2.

11. onTransition renamed to onNavigation

SchemaForm prop onTransition was renamed to onNavigation to keep up with React Router's new terminology.

Minor changes

12. Added transformResult

Instead of relying on callbacks, we now offer a more flexible transformResult option to performMutation and formAction. It is a function that receives a MutationResult and returns another result. Here's the type definition:

transformResult?: (
    result: MutationResult<Schema, D>,
  ) => MutationResult<Schema, D> | Promise<MutationResult<Schema, D>>

A common use case is to make conditional redirects with custom headers, like:

export const action = async ({ request }: Route.ActionArgs) =>
  formAction({
    request,
    schema,
    mutation,
    successPath: '/success',
    transformResult: (result) => {
      if (!result.success) {
        session.flash('error', 'Invalid username/password')

        throw redirect('/login', {
          headers: { 'Set-Cookie': await commitSession(session) },
        })
      }

      return result
    },
  })

The above example will throw a redirect in case of failures and return the result otherwise.

13. successPath now accepts async functions

You can now pass an async function to successPath on formAction, like:

export const action = async ({ request }: Route.ActionArgs) =>
  formAction({
    request,
    schema,
    mutation,
    successPath: async data => await calculateRedirectPath(data),
  })

Full Changelog: v2.3.0...v3.0.0

v2.3.0

07 Mar 16:59
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.2.1...v2.3.0

v2.2.1

06 Dec 14:39
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.2.0...v2.2.1

v2.2.0

27 Oct 20:26
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.1.0...v2.2.0

v2.1.0

01 Sep 12:39
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.0.0...v2.1.0

v2.0.0

17 Aug 13:17
Compare
Choose a tag to compare

🔥 Breaking changes

  • We now depend on domain-functions 2.x.

What's Changed

Full Changelog: v1.6.6...v2.0.0

v1.6.6

17 Aug 12:51
6e82068
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.6.5...v1.6.6

v1.6.5

25 Jul 17:47
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.6.4...v1.6.5

v1.6.4

04 Jul 02:02
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.6.3...v1.6.4

v1.6.3

10 Apr 14:06
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.6.2...v1.6.3