How to allow updating of elements instead of destroying and rendering the elements?
I have an array of 100 objects and an div is rendered for each object. Whenever the array changes, a new set of 100 divs are rendered for the new objects. How do I make it such that there are always 100 divs and whenever the array changes, the contents of these divs are changed instead of the divs being re-rendered (destroyed and created).
I'm doing this to improve performance and user experience
34 Replies
So the video above (Delay_in_Updating) shows what happens when I click a button to update the array. There is a delay before the current menu state (below the 'Categories' heading) is updated.
I understand that this is because it has to update the array and re-render the divs.
And the video above (Instant_Updating) shows what happens when I only put a dummy array with 100 empty objects so that no divs are destroyed and created when the button is pressed. As you can see, the current menu state (shown as text below the 'Categories' heading) updates instantly.
But how do I make the state update instantly (even if dummy data shows for the 100 divs) and when the divs are created, they display smoothly instead of the user waiting for an update which makes the app seem laggy.
Or if my question is impossible, how can I provide a better User Experience.
Use fine-grained reactivity as much as possible. Reuse object instances as much as possible.
Without having a look at your code, I cannot make more detailed suggestions.
Okay, I'm working on uploading a demo to codesandbox.
Here is the demo
https://codesandbox.io/p/sandbox/frosty-knuth-dlr3dp?file=%2Fsrc%2FApp.tsx
You can check out the comment in MediaList.tsx line 39
Kindly check it out. @Alex Lohr
I think that if you want to reuse the
div
's when replacing an array with another array having different content, you can use Index
instead of For
Conceptually For
will reuse the dom element for elements in the array that didn't change. In the function rendering an element, the content supplied as value and the index is a signal as it might change it's location when array elements are added and removed.
When inserting an element at index 0, it will create dom elements for that new element and all the others will be reused as they didn't change.
Index
on the other hand will always link the dom elements to the index array. In the function rendering an element, the index is a value, and the content is a signal (as it might change).
When inserting an element at index 0, it will update the existing dom element, and will need to do the same for all others, since they shift down (and will need to create a new dom element for last item).
Usually For
is the safer default as it's probably more efficient on average. If your usual use case is to replace the whole content of the array, Index
should perform better.Okay. I’ll try that out now.
Sorry, was abroad for some hours. I'm checking it out.
Okay. Thank you very much.
I used the but it doesn't work as expected. It only updates the UI when I fetch new data from the database to display. It doesn't update when I use previously saved data
By the way, if you want a good example on how to shape the reactive state for a pagination, have a look at https://github.com/solidjs-community/solid-primitives/blob/main/packages/pagination/src/index.ts#L82-L242
GitHub
solid-primitives/packages/pagination/src/index.ts at main · solidjs...
A library of high-quality primitives that extend SolidJS reactivity. - solidjs-community/solid-primitives
Index should update even on previously saved data, if you give it a new array with the previous objects.
Okay. I'll check it out now.
Maybe I should pass a function that returns the array?
In the codesandbox, I included an apiDemo.json file so we have a fixed media signal.
Here's a demo: https://primitives.solidjs.community/playground/pagination - unfortunately, the CSS for the demo is broken; I really need to fix that.
Solid Primitives
A library of high-quality primitives that extend SolidJS reactivity
Okay, I'll try it out.
@JCM I tried using a createMemo to create a new array but the Index still didn't work.
https://codesandbox.io/p/sandbox/frosty-knuth-dlr3dp?file=%2Fsrc%2FMediaList.tsx%3A50%2C33
In the meantime, I'll work on implementing the pagination primitive.
Index
and For
have behave differently internally, but the both render an array correctly. Swapping one for another can perform differently, but shouldn't change correctness.
It looks like the problem is in MediaCard. You break reactivity by assigning props to local variables. I didn't show the problem in the For
case as it was recreating the component from scratch, but it would show if you would use finer grained reactivity.
In short, try fix MediaCard first. As it's a big example, I just skimmed over it; not sure I got all the details right.Okay, I understand.
I'll fix it now.
I'll memoize the derived values
for a different idea from what has been mentioned:
you could use a virtual scrolling/ list component
the gist of it is that it only renders the elements that are in the viewport
https://github.com/minht11/solid-virtual-container
You need to create the cards outside the index/for, at the top level of the component for example, then they wont be rerenderd in the index/for loop
Yeah but the issue is I will have to implement spatial-navigation soon and implementing virtual scrolling + spatial nav is going to take quite a while/be difficult to get right.
i ran into this problem a few times
Okay, but how do I update the cards when the data changes if they're not in the index/for?
with a signal and createeffect
then inside the index yous use {mediaCard} instead of <MediaCard ... />
I don't really understand. Can I see what you mean?
You can copy my code from the codesandbox and edit it here in discord
where does the data change
looks like your passing in a media prop that doesnt ever change inside the component
The media prop in App.jsx never changes. But the property accessed by the MediaList page changes whenever you click on a Sidebar item.
none of the other files are loading so I cant see whats going on, but if the data is changing outside mediaList, and medialist is rerendering on a signal, then it wont work because you rerunning that component every time and creating the list of cards every time
You'll want to move your data fetching into the media list component so you dont have to rerender it every time.
MediaList won't re-render since Solid doesn't re-render components
Thank you @JCM and @Alex Lohr
I was able to use fine-grained reactivity along with
Index
to better update the divs instead of re-rendering them.
I have a better understanding of the term now.
Now, another issue I want to bring up is that the page
signal (which is updated when you click on a sidebar menu item) updates faster when I am not updating the 100 divs. I am outputting the signal to the screen (below the Categories
heading in the sidebar).
When I use the dummyData for the Index
, the page
signal updates faster because there are no other effects/memos to update in the UI.
But when I use the props.media
array, it takes a while for the page
UI output to update because 100 divs have to update along with it:
Yes, when rendering big lists and facing performance issues, virtualization is the way to go. There is also: https://tanstack.com/virtual/v3
it will rerender when the media signal in its props changes
Okay. But don't you think virtual lists will interfere with spatial navigation?
Also, how can I make it such that the
page
signal updates quickly while the media is still updating in the UI.
Unless, I create 2 page signals. One for the MediaCards updating and one for the sidebar/other things I need to update quickly.
It sounds weird to create 2 signals so I thought there may be a better way or something I'm missingNot sure what you mean with spatial navigation. If you use pagination, virtualization is probably not necessary as the amount of elements on the page won't be big. I have never combined virtualization with pagination, but it should work.
Signals are synchronous, so if you call a signal setter like
setPage
, all the dom will be updated when you reach the next line of code (unless it's batched).
Should there be data fetching in the background from the server that is asynchronous, a Resource
it the standard way to deal with that.Spatial Navigation involves navigating the UI using keyboard keys (or a remote). It usually involves getting a map of all the available elements and determining the next best one to navigate to depending on the direction key/button pressed so a virtual-list would interfere with that.
I'll implement the spatial-nav, then the virtual-list and see what happens.
Yes, I understand that. But in the case that there are a 1001 elements to update, how do I make sure 1 specific element is updated before the rest.
One way I thought of is by creating the illusion of a smoother UI by first rendering Skeleton Loading Cards before updating them to the actual cards.
So I update the Sidebar first, then after that I update the MediaCards or something like that.
What do you think?
Is there a better/smarter way?
For reference, I have a
createResource
in the original code that fetches the data for pages when there is no data to display. And since the Resource
is async, the page
signal updates instantly.
But when trying to access already saved data in the media signal, there is a delay.Admittedly, I haven't spend much time looking at the example, so I'm not really sure I understand the problem correctly.
But in the case that there are a 1001 elements to update, how do I make sure 1 specific element is updated before the rest.
Unless I misunderstand the problem, I don't think you should aim for that. The interface should visualize the data in a consistent way (eg. data-> UI). If you need to order the updates in function of importance or urgency, it's getting messy. Paging and/or virtualization should fix the performance issues.Okay, thank you for the advice. That means I’ll look at ways I can get a smoother change which means I’ll look into the virtualization next, then transitions.
Thank you to everyone who contributed and helped. 🤍