inertia icon indicating copy to clipboard operation
inertia copied to clipboard

FormHelper transform not working

Open mrmonat opened this issue 3 years ago • 7 comments

Versions:

  • @inertiajs/inertia version: 0.11.0
  • @inertiajs/inertia-react version: 0.8.0

Describe the problem:

The transform function of the form helper does not work.

Steps to reproduce:

const { data, setData, transform, post } = useForm({
        status: 'default',
        foo: 'bar',
    })

function handleDraft(event) {
        event.preventDefault()
        transform(data => ({ ...data, status: 'draft' }))
        post('/test')
 }

function handleSave(event) {
        event.preventDefault()
        transform(data => ({ ...data, status: 'open' }))
        post('/test')
}
Route::post('/test', function (Request $request) {
    return dd($request->all());
});

In both cases (handleSave or handleDraft called) the result from the laravel controller is:

^ array:2 [[▼]()
  "status" => "default"
  "foo" => "bar"
]

mrmonat avatar Mar 15 '22 16:03 mrmonat

+1

i got same problem

muh-hizbe avatar Apr 01 '22 04:04 muh-hizbe

I tested again with react 18 instead of react 17, but the problem still remains

mrmonat avatar Apr 01 '22 08:04 mrmonat

for now i use alternatif way,

const [data, setData, reset] = useForm();
.
.
.
Inertia.post('url', {...data, custom_field: custom_value})

muh-hizbe avatar Apr 01 '22 11:04 muh-hizbe

Not sure if this is the same issue, but I'm having similar problems with a Rails backend. I've found that if my server responds with anything other than a success, the transform method doesn't run. This makes debugging incredibly difficult and confusing. I was hoping to be able to throw some values at my server and see what shape they arrived, then make it look the way Rails wants it to look. Instead it seems I have to build my transforms in another method and then move it over to the transform block once it's ready?

To make it even more confusing, console.log doesn't output anything from within the transform method, regardless of server response. Likewise, inspecting form data after running transform doesn't reflect the transformations, so there's no way to view the shape of your data after it's been transformed. The only way I've found is by inspecting the body of the request in the Network tab of dev tools.

It would really be great to have access to the shape of the transformed data client side before it gets sent off to the server, and even greater if the transform was applied to the request regardless of server response.

aviemet avatar Apr 27 '22 21:04 aviemet

I just stumbled across the same issue. Is there any update on when this would be fixed?

reinvanimschoot avatar Sep 20 '22 10:09 reinvanimschoot

Why is the issue not being resolved? It's been eight months!

You have a PR #1171. Why is not merged?

den1n avatar Oct 14 '22 06:10 den1n

I am facin the same issue with vue3

wamuyu-felix avatar Nov 03 '22 17:11 wamuyu-felix

I am facin the same issue with vue3

so am i

Dolaned avatar Nov 13 '22 14:11 Dolaned

Is there no fix for this issue still?

mahangm avatar Dec 08 '22 23:12 mahangm

Still not fixed?

sbc640964 avatar Jan 29 '23 13:01 sbc640964

I'm currently facing this issue too

Jamesking56 avatar Feb 04 '23 20:02 Jamesking56

Issue is still present in v1.0.

den1n avatar Feb 14 '23 04:02 den1n

@rsdrsd That works really well thanks, I was getting the validation errors not coming back before but with your workaround the error messages now work!

Jamesking56 avatar Feb 28 '23 20:02 Jamesking56

Any update?

jerooomewolf avatar May 08 '23 12:05 jerooomewolf

Hope this will be fixed, there's a pr for this

pikapikamart avatar May 15 '23 08:05 pikapikamart

I thought I might offer a solution for those searching for something quick. I built my form solution to be reusable, and published it to npm to make things easier for me. It includes a re-work of useForm which fixes the transform issue and can be used as a direct drop-in for the original hook. It does a few other things, but if you just need a fix for this issue, it might be of use. You can find it here: https://github.com/aviemet/useInertiaForm

Please feel free to use it, copy it, steal it, improve it, etc.

aviemet avatar May 21 '23 16:05 aviemet

@aviemet Really neat!! Just looked at the repo and it looks really nice. Will try to use it in another project!

pikapikamart avatar May 22 '23 01:05 pikapikamart

Hey! Thanks so much for your interest in Inertia.js and for sharing this issue/suggestion.

In an attempt to get on top of the issues and pull requests on this project I am going through all the older issues and PRs and closing them, as there's a decent chance that they have since been resolved or are simply not relevant any longer. My hope is that with a "clean slate" me and the other project maintainers will be able to better keep on top of issues and PRs moving forward.

Of course there's a chance that this issue is still relevant, and if that's the case feel free to simply submit a new issue. The only thing I ask is that you please include a super minimal reproduction of the issue as a Git repo. This makes it much easier for us to reproduce things on our end and ultimately fix it.

Really not trying to be dismissive here, I just need to find a way to get this project back into a state that I am able to maintain it. Hope that makes sense! ❤️

reinink avatar Jul 28 '23 01:07 reinink

Any update i m stuck on this issue

Kamleshpaul avatar Nov 25 '23 07:11 Kamleshpaul

I had the same problem, so, I just change my focus, I tried post(url,{ transform:(data)=>({...data,{'field':value}}) })

But din't work, so, how can I do the same using inertia? Using router.visit, so router have shortcut, then I changed post from useForm to

router.post(url,{'field':'value'},{ onSuccess: (page) => { // something here }, }) Something that fucked me was losing the processing variable from useForm though, instead using react setState I got the same effect using setState on onBefore and in onSuccess

gamboitaygb avatar Mar 12 '24 20:03 gamboitaygb

Due to the way the bug is introduced, there unfortunately isn't any way to circumvent it and still have the benefits of the useForm methods. My only suggestions are to import of the fixed versions that have come out of this 2 year long bug fix request. One is my own and another was created by a commenter on the open PR fix for this bug.

I created a package which fixes this issue and also adds support for nested form data. If you want to use the nested data solution, or don't mind the slightly larger import size, that could be useful for you.

Another person left a comment that they created a new npm package from a fork of Inertia where the only change is properly memoizing the transform method (which fixes the bug).

aviemet avatar Mar 12 '24 22:03 aviemet

Given the current implementation of transform, the key detail here is that your calls to transform need to be located in the body of the component (so it gets called on every render), rather than inside a handler callback (see discussion here https://github.com/inertiajs/inertia/pull/1491#issuecomment-2181524223).

Here's a workaround for the OP:

const { data, setData, transform, post } = useForm({
  status: "default",
  foo: "bar",
});

// Store the current "status" in a ref
const statusRef = useRef("default");

// Place the transform call in the function body
transform((data) => ({ ...data, status: statusRef.current }));

function handleDraft(event) {
  event.preventDefault();
  statusRef.current = "draft";
  post("/test");
}

function handleSave(event) {
  event.preventDefault();
  statusRef.current = "open";
  post("/test");
}

derrickreimer avatar Jun 21 '24 14:06 derrickreimer

This is the workaround I have come up with for a slightly different use case.

I have articles which may be in draft state - they can always be saved (with the draft state left unchanged)

But I also want buttons to publish or unpublish which should change the draft status and then save

I originally thought I could use transform for this but realised it doesn't work that way.

Instead I added a flag to the "data" object to indicate the status has saved - now I set this flag and change the draft status.

When the component re-renders the flag is detected (set back to false) and the save is triggered - now with the updated "data" object.

I haven't used this in production yet - it works in testing and I'd be interested in whether people thing this is a good approach.

    // change draft state and trigger a save 
    const publish = () => {
        setData({ ...data, changedPublishedState: true, "draft": false });
    }
    const unpublish = () => {
        setData({ ...data, changedPublishedState: true, "draft": true });
    }
    // save without changing state 
    const submit = (e) => {
        e.preventDefault();
        save();
    }

    const save = () => {
        post(route("article.update", { site: site.id, article: article.id }),
            {
                preserveScroll: false,
                preserveState: (page) => Object.keys(page.props.errors).length,
                onSuccess: () => {
                    setTab("edit");
                }
            });
    };
    // triggered after a re-render when data.draft status has been changed
    if (data.changedPublishedState) {
        setData("changedPublishedState", false);
        save();
    }

seanburlington avatar Jul 20 '24 21:07 seanburlington