inertia icon indicating copy to clipboard operation
inertia copied to clipboard

transform method in useForm hook doesn't work

Open aviemet opened this issue 1 year ago • 16 comments

Version:

  • @inertiajs/react version: 1.0.9

Describe the problem:

I'm re-reporting an issue which was closed in the recent reset of issues, the original is #1131

When using useForm from the React package, the transform method doesn't work. The method is defined in the body of the useForm component, and thus is re-initialized upon re-renders to be the original "placeholder" method.

The transform method needs to be memoized in some way. I submitted #1491 which I've tested and it addresses this issue. It stores the transform method in a ref so that when it's value is updated it persists through re-renders.

aviemet avatar Jul 31 '23 17:07 aviemet

I fixed this using a ref hook.

const transformRef = useRef({} as any);
useEffect(() => {
       transformRef.current = (data: any) => {};
       transform(transformRef.current)
});

saeidalidadi avatar Aug 19 '23 16:08 saeidalidadi

v1.0.11 - the problem persists. If no changes have been made to the form fields, the transform() method is ignored thus not being called. =(

qMaDMaNp avatar Sep 12 '23 00:09 qMaDMaNp

This problem still persists

Zagoman avatar Nov 06 '23 12:11 Zagoman

Nearly 3 years and this very basic error still persists. It genuinely makes me fear for the longevity of this amazing project. Inertia is the glue that binds the frontend and backend together and is exactly how I want to build projects, I really hope this isn't a foretelling of an eventual death of the project.

For anyone who needs a quick drop-in, I've built out a replacement for useForm which fixes the transform bug, and also adds a few other features. Please feel free to use it, improve it, steal it, etc. Hopefully one day it won't be necessary.

aviemet avatar Nov 06 '23 18:11 aviemet

@reinink @jessarcher can we get a confirmation you are aware of this bug and that there is a PR open for it? I'm not sure why this bug has been ignored for years now. It's core functionality to one of the best features of this library.

https://github.com/inertiajs/inertia/pull/1718

rossity avatar Nov 13 '23 16:11 rossity

Surprisingly, it was working fine for me until I made an update and it stopped working, I undid all the changes and it still not working.

ThePlatinum avatar Nov 23 '23 07:11 ThePlatinum

I fixed this using a ref hook.

const transformRef = useRef({} as any);
useEffect(() => {
       transformRef.current = (data: any) => {};
       transform(transformRef.current)
});

Allo! Can anyone reading explain this code snippet for me?

I tried it, and variations thereof, all it did was clear all data on every submit. 😕

I've been looking into this for a few hours and here's a summary of what I think I understand so far:

  1. That code snippet above can allegedly work around problem.
  2. This PR looks similar to the code snippet but is deeper: https://github.com/inertiajs/inertia/pull/1718
  3. This package could also work: https://github.com/aviemet/useInertiaForm

As much as option 3 is great, kudos to the dev!, for me, it would be most interesting to get option 1 working, a temporary patch as we faithfully await option 2 to be merged.

More hints for idea 1 please? Thanks.

dac514 avatar Dec 28 '23 15:12 dac514

Following up on using saeidalidadi's snippet. I got something working.

Before:

transform((data) => {
    // REPLACE ME: your transform routine goes here
    return data
});

After:

import { useRef, useEffect } from 'react';
// ...
const transformRef = useRef({} as any);
useEffect(() => {
       transformRef.current = (data: any) => {
           // REPLACE ME: your transform routine goes here
           return data
       };
       transform(transformRef.current)
}, [data]);

Then, add onChange={(e) => setData('REPLACE_ME', e.target.value)} in as many places as possible to trigger re-renders so that your routine gets reloaded if ever it were to be replaced by the original inertia.js placeholder code.

dac514 avatar Jan 08 '24 21:01 dac514

Surprisingly, it was working fine for me until I made an update and it stopped working, I undid all the changes and it still not working.

Same here. First it is working perfectly fine until I make some modification to the code. But undo changes is not helping either.

One thing I noticed that it is working after the code has an error and fix it. But It is not working after refresh.

NaungYeHtet avatar Jan 28 '24 06:01 NaungYeHtet

@reinink Why isn't this bug fixed? The bug exists already for ages and it's really annoying that the transform method isn't working properly.

threesil avatar Apr 21 '24 12:04 threesil

Is there any update on this issue?

hasan-almujtaba avatar Jun 20 '24 10:06 hasan-almujtaba

I've been looking into this one and believe I arrived at the crux of the confusion. The transform function is designed to be called in the body of the component, not inside a handler function. As long as you place it in the body, you should be good to go. To give a concrete example:

This works:

const Page = () => {
  const { post, transform } = useForm({});

  transform((data) => ({ ...data, foo: "bar" }));
  
  const handleSubmit = (e) => {
    e.preventDefault();
    post("/route");
  }

  // ...
}

This does not work:

const Page = () => {
  const { post, transform } = useForm({});
  
  const handleSubmit = (e) => {
    e.preventDefault();
    transform((data) => ({ ...data, foo: "bar" }));

    // A re-render gets triggered somewhere in `post`, 
    // which wipes out the `transform` call just made
    post("/route");
  }

  // ...
}

(Discussed here: https://github.com/inertiajs/inertia/pull/1491#issuecomment-2181524223)

derrickreimer avatar Jun 21 '24 13:06 derrickreimer

Then, as pointed in #1491, I think it should be noted in the docs that transform is the only method of the useForm hook that must be called from the component body and not from inside any component's handler function. That would save much time to people trying to use it for the first time...

andcl avatar Jun 27 '24 11:06 andcl

Thanks @andcl. Could you maybe attempt a PR to the docs?

driesvints avatar Jun 28 '24 07:06 driesvints

Done. https://github.com/inertiajs/inertiajs.com/pull/348

andcl avatar Jul 10 '24 17:07 andcl

Even if the originally intended behavior of the transform method is to be used in the component's body, It will be much more useful if could be used like #1131. At least for me it feels hacky storing a value in a ref or somewhere else to be later used in the transform.

Maybe a solution like the one proposed here could also work.

Another posibility could be to merge the form state with the data provided in the submit method. I would make this have precedence over the current state so it can be overriden. Currently the data is ignored and only the form state is submitted.

form.post(route('my-route'), {
  data: {foo: 'bar'} // Merge this with the current form state
})

agustinzamar avatar Jul 22 '24 23:07 agustinzamar