> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# Deferred Props

Tính năng deferred props của Inertia cho phép trì hoãn việc tải một số dữ liệu trang cho đến sau khi lần render ban đầu hoàn tất. Điều này giúp cải thiện hiệu năng cảm nhận bằng cách cho phép trang ban đầu render nhanh nhất có thể.

## Phía máy chủ

Để defer một prop, bạn có thể dùng method `Inertia::defer()` khi trả response. Method nhận callback trả dữ liệu prop. Callback sẽ được thực thi trong một request riêng sau lần render trang ban đầu.

```php theme={null}
Route::get('/users', function () {
    return Inertia::render('Users/Index', [
        'users' => User::all(),
        'roles' => Role::all(),
        'permissions' => Inertia::defer(fn () => Permission::all()),
    ]);
});
```

### Nhóm request

Mặc định, toàn bộ deferred prop được fetch trong một request sau khi page ban đầu render. Tuy nhiên, bạn có thể fetch prop song song bằng cách nhóm chúng.

```php theme={null}
Route::get('/users', function () {
    return Inertia::render('Users/Index', [
        'users' => User::all(),
        'roles' => Role::all(),
        'permissions' => Inertia::defer(fn () => Permission::all()),
        'teams' => Inertia::defer(fn () => Team::all(), 'attributes'),
        'projects' => Inertia::defer(fn () => Project::all(), 'attributes'),
        'tasks' => Inertia::defer(fn () => Task::all(), 'attributes'),
    ]);
});
```

Trong ví dụ trên, props `teams`, `projects` và `tasks` được fetch trong một request, còn prop `permissions` được fetch song song trong request riêng. Tên group là chuỗi tùy ý và có thể đặt theo ý bạn.

## Phía client

Ở phía client, Inertia cung cấp component `Deferred` để quản lý deferred props. Component tự động chờ các deferred prop được chỉ định có sẵn trước khi render children.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Deferred } from "@inertiajs/vue3";
  </script>

  <template>
    <Deferred data="permissions">
      <template #fallback>
        <div>Loading...</div>
      </template>

      <div v-for="permission in permissions">
        <!-- ... -->
      </div>
    </Deferred>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Deferred } from "@inertiajs/react";

  export default () => (
    <Deferred data="permissions" fallback={<div>Loading...</div>}>
      <PermissionsChildComponent />
    </Deferred>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Deferred } from '@inertiajs/svelte'

      let { permissions } = $props()
  </script>

  <Deferred data="permissions">
      {#snippet fallback()}
          <div>Loading...</div>
      {/snippet}

      {#each permissions as permission}
          <!-- ... -->
      {/each}
  </Deferred>
  ```
</CodeGroup>

## Nhiều deferred prop

Nếu cần chờ nhiều deferred prop cùng có sẵn, bạn có thể truyền một array vào prop `data`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Deferred } from "@inertiajs/vue3";
  </script>

  <template>
    <Deferred :data="['teams', 'users']">
      <template #fallback>
        <div>Loading...</div>
      </template>

      <!-- Props are now loaded -->
    </Deferred>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Deferred } from "@inertiajs/react";

  export default () => (
    <Deferred data={["teams", "users"]} fallback={<div>Loading...</div>}>
      <ChildComponent />
    </Deferred>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Deferred } from '@inertiajs/svelte'

      let { teams, users } = $props()
  </script>

  <Deferred data={['teams', 'users']}>
      {#snippet fallback()}
          <div>Loading...</div>
      {/snippet}

      <!-- Props are now loaded -->
  </Deferred>
  ```
</CodeGroup>

## Chỉ báo đang reload

Khi deferred prop đang được reload qua partial reload, component `Deferred` cung cấp boolean `reloading` qua slot. Điều này cho phép hiển thị loading indicator trong khi vẫn giữ dữ liệu đã tải trước đó.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Deferred } from "@inertiajs/vue3";
  </script>

  <template>
    <Deferred data="permissions" #default="{ reloading }">
      <template #fallback>
        <div>Loading...</div>
      </template>

      <div :class="{ 'opacity-50': reloading }">
        <div v-for="permission in permissions">
          <!-- ... -->
        </div>
      </div>
    </Deferred>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Deferred } from "@inertiajs/react";

  export default () => (
    <Deferred data="permissions" fallback={<div>Loading...</div>}>
      {({ reloading }) => (
        <div className={reloading ? "opacity-50" : ""}>
          <PermissionsChildComponent />
        </div>
      )}
    </Deferred>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Deferred } from '@inertiajs/svelte'

      let { permissions } = $props()
  </script>

  <Deferred data="permissions">
      {#snippet fallback()}
          <div>Loading...</div>
      {/snippet}

      {#snippet children({ reloading })}
          <div class:opacity-50={reloading}>
              {#each permissions as permission}
                  <!-- ... -->
              {/each}
          </div>
      {/snippet}
  </Deferred>
  ```
</CodeGroup>

Prop `reloading` là `false` ở lần tải đầu và chuyển thành `true` khi partial reload đang chạy cho các deferred key. Nó trở lại `false` khi reload hoàn tất.

## Xử lý lỗi

Mặc định, exception ném ra trong lúc resolve deferred prop sẽ tạo error response. Bạn có thể yêu cầu Inertia rescue các exception này bằng cách truyền `rescue: true` vào `Inertia::defer()`.

```php theme={null}
Route::get('/users', function () {
    return Inertia::render('Users/Index', [
        'permissions' => Inertia::defer(fn () => Permission::all(), rescue: true),
    ]);
});
```

Khi deferred prop được rescue, nó bị loại khỏi response và exception được báo qua exception handler của Laravel.

Ở phía client, bạn có thể cung cấp slot `rescue` cho component `Deferred` để render fallback UI khi prop tải thất bại.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Deferred, router } from "@inertiajs/vue3";
  </script>

  <template>
    <Deferred data="permissions">
      <template #fallback>
        <div>Loading...</div>
      </template>

      <template #rescue="{ reloading }">
        <div>
          <p>Failed to load permissions.</p>
          <button :disabled="reloading" @click="router.reload({ only: ['permissions'] })">Retry</button>
        </div>
      </template>

      <div v-for="permission in permissions">
        <!-- ... -->
      </div>
    </Deferred>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Deferred, router } from "@inertiajs/react";

  export default () => (
    <Deferred
      data="permissions"
      fallback={<div>Loading...</div>}
      rescue={({ reloading }) => (
        <div>
          <p>Failed to load permissions.</p>
          <button disabled={reloading} onClick={() => router.reload({ only: ["permissions"] })}>Retry</button>
        </div>
      )}
    >
      <PermissionsChildComponent />
    </Deferred>
  );
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Deferred, router } from '@inertiajs/svelte'

      let { permissions } = $props()
  </script>

  <Deferred data="permissions">
      {#snippet fallback()}
          <div>Loading...</div>
      {/snippet}

      {#snippet rescue({ reloading })}
          <div>
              <p>Failed to load permissions.</p>
              <button disabled={reloading} onclick={() => router.reload({ only: ['permissions'] })}>Retry</button>
          </div>
      {/snippet}

      {#each permissions as permission}
          <!-- ... -->
      {/each}
  </Deferred>
  ```
</CodeGroup>

Rescue state được giữ cho tới khi bạn chủ động reload prop đã rescue. Slot `rescue` nhận boolean `reloading`, cho phép disable nút retry hoặc hiển thị loading indicator trong khi reload đang diễn ra.

## Kết hợp với Once Props

Bạn có thể nối modifier `once()` vào deferred prop để đảm bảo dữ liệu chỉ được resolve một lần và được phía client ghi nhớ qua các lần điều hướng tiếp theo.

```php theme={null}
return Inertia::render('Dashboard', [
    'stats' => Inertia::defer(fn () => Stats::generate())->once(),
]);
```

Để biết thêm về once props, xem tài liệu [once props](/v3/data-props/once-props).

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v3 chính thức](https://inertiajs.com/docs/v3/data-props/deferred-props). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
