DataViews: lazy load elements data#71930
DataViews: lazy load elements data#71930dinhtungdu wants to merge 15 commits intoWordPress:trunkfrom
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
packages/dataviews/src/types.ts
Outdated
| export type FieldElementsSource< Value extends any = any > = | ||
| | Option< Value >[] | ||
| | Promise< Option< Value >[] > | ||
| | ( () => Option< Value >[] | Promise< Option< Value >[] > ); | ||
|
|
There was a problem hiding this comment.
This could be simplified to:
export type FieldElementsSource =
| Option[]
| Promise< Option[] >
| ( () => Promise< Option[] > );I was thinking that elements could be either Option[] or an async function, as the async function enables the framework to trigger retrieval when necessary. However, having the Promise<Option[]> as well enables consumers to trigger retrieval instead. Nothing against it, I am just curious if you have any more thoughts about this.
There was a problem hiding this comment.
Promise<Option[]> gives consumers an easy path to get started; it makes the code less verbose than an async function. That's the only benefit of accepting a Promise. In my opinion, it doesn't pollute the type, so I think we should accept that.
Regarding the async function, I think we should still support returning a synchronous array. By doing so, we can better accommodate use cases when the data is already available or computed synchronously, for example, derived lists built from other props or localStorage (not a fan, though :D). We're giving consumers flexibility without trading much complexity, so I think we should do it.
There was a problem hiding this comment.
After consideration, I decided to support only array or async, as you suggested. We should start by focusing on what matters most.
| ); | ||
|
|
||
| const fieldElements = field?.elements ?? []; | ||
| const { elements: fieldElements, isResolving } = useFieldElements( |
There was a problem hiding this comment.
This approach triggers retrieval at the leaf: elements are fetched when the components that need them are mounted (dataform controls, filters). This is good for minimizing memory/computing. However, I am concerned about how this impacts user perceived performance. There are some other places where we should also trigger "leaf" retrieval: the render methods of the field types.
What would you think of triggering the retrieval (call useFieldElements) in normalizeFields instead? That way, the elements may already be loaded when the user opens a filter, and we centralize this process for all components that work with elements (render, filters, etc.).
There was a problem hiding this comment.
What would you think of triggering the retrieval (call useFieldElements) in normalizeFields instead? That way, the elements may already be loaded when the user opens a filter, and we centralize this process for all components that work with elements (render, filters, etc.).
We should consider the trade-offs involved. While this can make the UI feel snappier and faster, I have some concerns with this approach.
- Firstly, scalability is an issue. A list can have many filters, and fetching all data at once can lead to performance issues.
- Secondly, we know that we need search and pagination. So we still need to retrieve the data at the leaf. We are creating two mechanisms for fetching the data, and I'm afraid it makes our codebase more complex.
- Lastly, if we want to retrieve everything in a normalized format, we may need to introduce another cache layer to store data. We have one for entities already. I don't think we need another one.
Given these pros and cons, I believe it's best to keep retrieval at the leaf for now. wdyt?
There was a problem hiding this comment.
Let's try this way and re-evaluate when we have everything working (searching, etc.). This means we need fetching them every time they are potentially used, because it may be the first time. For example, we need to fetch them in render as well.
There was a problem hiding this comment.
I corrected some words in my comment. I used dictation but missed fixing them.
Let's try this way and re-evaluate when we have everything working (searching, etc.).
@oandregal So you mean moving the fetch to normalizeFields, right?
There was a problem hiding this comment.
No, leave it as you proposed in this PR (fetching at the leaves). But we need to cover all use cases (every place that uses elements, including render).
| error, | ||
| reloadElements, |
There was a problem hiding this comment.
Do we need these? I don't think we should retrigger element retrieval. Though perhaps error could be displayed in controls and render as in Elements could not be loaded.
There was a problem hiding this comment.
Do we need these?
We don't have it now. But I believe we need retriggering for search and pagination. I can remove it for now and reconsider the API when adding search and pagination support.
|
I wanted to share this use case that may be worth considering as well for this PR or for a follow-up: #70834 (comment) |
85ae0c0 to
3fd04c2
Compare
Yes, I agree we will need it. In the PR description, I also mentioned search and pagination. I want to address that in a dedicated PR to keep the size reasonable. My rough idea is that we can pass the search keyword and/or current to the function for consumers to use like: Then pass that data inside the hooks to the |
ee78707 to
0c48688
Compare
46e5514 to
82cba9e
Compare
|
I've spent some time today exploring this problem and prepared #72254 Working:
Not working:
I need to look at this a bit more, but I'm not convinced we can solve this problem without making validation reactive first. |
d03e582 to
3914e19
Compare
3914e19 to
c9b1642
Compare
|
@oandregal pushed some updates to this PR:
About the validation, I agree, we need to make the validation work with async first. We can solve it using a custom hook, like dinhtungdu#68. It works, and the implementation is simple as we're now loading everything. But it will get more complex when we support pagination. Screen.Recording.2025-10-13.at.13.16.34.mov |
|
Thanks for adding support for authors. All DataForm controls need to communicate loading status, so users know what's going on. This is the current experience: Screen.Recording.2025-10-13.at.11.58.40.movOver at #71412 I explored implementing async elements as a separate function Though, for this to land, we need to solve validation first. I also explored async validation a few weeks ago at #71412 but ran into some issues due to component validation works. I'll investigate what we can do here and will report back. |
|
@dinhtungdu one thing we can do is removing the elements checks from |
|
@dinhtungdu I've updated #71412 (comment) (that enables async validation) and engaged folks to review. |
|
Closing in favor of #72254 |
What?
Part of #70834
This PR updates the
elementsprop to accept a static array or Promise factory, allowing the fetching of theelementsdata on demand.Why?
See #70834
How?
elementsprop to accept Promise or factory, so the prop now accepts three types of data:useFieldElementswhich returns:elementsisResolvingerrorNotes:
Testing Instructions
I've included the new story for lazy-loaded elements and unit tests to verify the new behavior.