-
Notifications
You must be signed in to change notification settings - Fork 538
Description
Sometimes we need screens with a lot of data. Views that bring together a lot of concepts, and require a lot of queries and heavy lifting to load. We currently have the only option to make reloads more efficient, but this doesn't help the initial load.
Another place where this is useful is when you're loading data from an external API in real time, like a report from Stripe. That API call might take a few seconds to come through, so you'd want to send a response before the call is completed. With Inertia, this means you'd send a response without data, and do an API call afterwards.
public function index()
{
return Inertia::render('Report');
}
public function transactions()
{
return ['transactions' => Stripe::getTransactions()];
}<script>
export default {
props: [],
data: () => ({ transactions: null }),
created() {
fetch('transactions')
.then(response => response.json())
.then(data => {
this.transactions = data.transactions
})
}
}
</script>This works, but with Inertia we should be able to do better. With some creative coding and partial reloads, we can already get this done without the API endpoint.
public function index()
{
return Inertia::render('Report', [
'transactions' => in_array('transactions', explode(',', Request::header('X-Inertia-Partial-Data'))))
? Stripe::getTransactions()
: null,
]);
}<script>
export default {
props: ['transactions'],
created() {
this.$inertia.reload({ only: ['transactions'] })
}
}
</script>This returns null for transactions on the first visit, and calls Stripe::getTransactions() when partially reloading. The first visit will be fast, when explicitly requesting transactions it'll be slow.
Improvements here are:
- No more additional API endpoint
- No more internal state, all props
This could become a first class citizen. In that case, the controller would look cleaner, and Inertia would take care of the second request.
public function index()
{
return Inertia::render('Report', [
'transactions' => Inertia::defer(fn => Stripe::getTransactions()),
]);
}<script>
export default {
props: ['transactions']
}
</script>I think defer or lazy communicates the feature pretty well. I prefer defer here, so we can use lazy for the next step.
Additional feature: lazy loading
The defer feature could be extended to give the user control when the second load occurs. This could be useful to pair with IntersectionObserver, so you would only load additional data when the visitor is about to see it.
The hardest part about this is setting up a proper API.
public function index()
{
return Inertia::render('Report', [
'transactions' => Inertia::lazy(fn => Stripe::getTransactions()),
]);
}<script>
export default {
props: ['transactions'],
created() {
new IntersectionObserver(() => {
this.$inertia.reload({ only: ['transactions' ]})
}, { … })
}
}
</script>If this was a thing, we could add a more fitting method to Inertia, like this.$inertia.load('transactions') to hide the implementation details of partial reloading.
Additional feature: parallel versus serial updates
This one could bring a lot of problems, and is probably over the top. But in some cases it might be more useful to load the additional data in parallel.
public function index()
{
return Inertia::render('Dashboard', [
'metricA' => Inertia::deferred(fn => …),
'metricB' => Inertia::deferred(fn => …),
'metricC' => Inertia::deferred(fn => …),
'metricD' => Inertia::deferred(fn => …),
'metricE' => Inertia::deferred(fn => …),
]);
}