inertiajs / inertia

Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers.
https://inertiajs.com
MIT License
6.12k stars 415 forks source link

Do not NULL lazyloaded props when redirecting back to the same page #1629

Closed lpeterke closed 2 months ago

lpeterke commented 1 year ago

Discussed in https://github.com/inertiajs/inertia/discussions/1628

Originally posted by **lpeterke** July 31, 2023 Imagine a "Home" page that displays a long list of items, which are loaded using `Inertia::lazy()`. The "Home" page might also have a child component that sends a simple POST, for example "Subscribe to our newsletter". After the action is performed, the NewsletterController simply redirects using `return redirect()->back()`. Inertia will then `NULL` all page props that utilize `Inertia::lazy()`. However, this feels rather buggy, as we are still on the same page component and nothing should tamper with the lazyloaded props, until a new reload is triggered from code. Previous discussion: https://github.com/inertiajs/inertia/issues/863
Riley19280 commented 10 months ago

I also require this functionality. I have a modal form that lazy fetches properties before opening, but when redirecting back on a validation form error in Laravel, those properties are seemingly cleared, causing the modal to close and re-fetch those properties. Desired behavior would be to keep these lazy properties around

Travis-TaxDigital commented 10 months ago

I agree. I have the same issue with building a Stripe billing portal. Historical Invoices & Payment methods are on LazyLoad using the OnMounted() method to speed everything up as they are time-consuming. When using return back(0 they should persist or, at the very least, be able to reload them. Any workaround?

lpeterke commented 10 months ago

I haven't implemented a workaround yet. Probably would be a mixin for Vue that manages copies of the lazy props:

  1. Have an Array "lazy_props" with all page Prop names that are lazyloaded.
  2. Programmatically register watchers for all values in "lazy_props"
  3. Have the watchers copy value changes into data attributes
  4. Provide computed properties that you would use instead of accessing the lazyloaded props directly
Travis-TaxDigital commented 9 months ago

Hi guys.

I've found a workaround that I have been able to implement (I only use lazy loading on one section of my app). There might be a better workaround as I am only new to programming. But it might help some others or the project team. If anyone sees any issue with it, please don't hesitate to let me know.

Controller.php

public function updatePaymentMethod(Request $request)
    {
        auth()->user()->currentTeam->updateDefaultPaymentMethod($request->get('pm_id'));
        return back(303)
        ->with([
            'message' => 'Your default payment method was updated',
            'type' => 'success',
            'title' => 'Success',
            'reload' => true,
        ]);
    }

HandleInertiaRequests.php

public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'flash' => function () use ($request) {
                return [
                    'message' => fn() => $request->session()->get('message'),
                    'type' => fn() => $request->session()->get('type'),
                    'title' => fn() => $request->session()->get('title'),
                ];
            },
           'reload' => function () use ($request) {
                return  fn() => $request->session()->get('reload');
            }
        ], [
            'shouldInterpolate' => true,
        ]);
    }

Vue Component (i.e. Show.vue) using Vue3 Composition API


// Lazy Load Initial Props
onMounted(() => {
    router.reload({ only: ['historicalInvoices'] });
})

// Set-up Inertia Page Object to Get Shared Data
const page = usePage();

// Watch for set Reload prop. If True Partial lazy Load Reload.
watch(() => page.props.reload,
    () => {
        console.log(page.props.reload);
        if (page.props.reload) {
            router.reload({ only: ['historicalInvoices'] });
        }
    }
)

I found that the key items were:

  1. Using the getter watch source (if that's the correct terminology) as outlined in the vue docs here
  2. Setting 'shouldInterpolate' => true, in the HandleInertiaRequests <= I found this on a forum and couldn't find it in the inertia or laravel docs. TBH, I don't know what it does, but it allowed multiple renders when doing multiple calls. I.e. deleting two separate payment methods (very useful for the flash messages also).
samuelhgf commented 2 months ago

Anyone with a workaround for this? Having the same issue here using React. The quick fix was not use the lazy anymore :'(

driesvints commented 2 months ago

Hey all. It seems this is more of a feature request. If you want, you can always attempt a PR. Thanks.