S
SolidJS•3mo ago
Mr Void

Prevent re-rendering many components when object in store-array is updated

I have a grid view where multiple card components are rendered. The issue is that all cards are re-rendered when one of the cards' data is updated in the store. Is there a way to prevent re-rendering everything when one object in the array is modified? This is how the card is modified:
updateCard(flashcard: Flashcard) {
setState("flashcards", "collections", (collections: FlashcardCollection[]) => {
const tempCollections = [...collections]

return tempCollections.map((collection) => {
const tempCollection = {...collection}
const cards = [...tempCollection.flashcards]

tempCollection.flashcards = cards.map((currentCard) => {
if (currentCard.id !== flashcard.id) {
return currentCard
}

return Object.assign({}, currentCard, flashcard)
})

return tempCollection
})
})
}
updateCard(flashcard: Flashcard) {
setState("flashcards", "collections", (collections: FlashcardCollection[]) => {
const tempCollections = [...collections]

return tempCollections.map((collection) => {
const tempCollection = {...collection}
const cards = [...tempCollection.flashcards]

tempCollection.flashcards = cards.map((currentCard) => {
if (currentCard.id !== flashcard.id) {
return currentCard
}

return Object.assign({}, currentCard, flashcard)
})

return tempCollection
})
})
}
and here is how i load & filter cards
const currentCollection = createAsync<FlashcardCollection>(async (prev) => {
const collection: FlashcardCollection
= store.flashcards.collections.find(
(collection) => collection.id === params.collection_id
)

return unwrap(collection)
})

const filteredCards = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
return coll.flashcards.filter(card => /* filtration condition here */)
})
const currentCollection = createAsync<FlashcardCollection>(async (prev) => {
const collection: FlashcardCollection
= store.flashcards.collections.find(
(collection) => collection.id === params.collection_id
)

return unwrap(collection)
})

const filteredCards = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
return coll.flashcards.filter(card => /* filtration condition here */)
})
<For each={filteredCards()}>
{(flashcard) => <Card data={flashcard} /> }
</For>
<For each={filteredCards()}>
{(flashcard) => <Card data={flashcard} /> }
</For>
8 Replies
REEEEE
REEEEE•3mo ago
You're not changing just one object, you're replacing the whole array it would be better to do
setState("flashcards", "collections", p => p.find(card => card.id === flashcard.id), reconcile(flashcard))
setState("flashcards", "collections", p => p.find(card => card.id === flashcard.id), reconcile(flashcard))
This should work but you'll have to check Alternatively use produce
Mr Void
Mr VoidOP•3mo ago
I'm not sure how to apply this to the following structure: store -> FlashcardCollection[] -> Flashcard[]:
export type FlashcardCollection = {
flashcards: Flashcard[]
// ...
}
export type FlashcardCollection = {
flashcards: Flashcard[]
// ...
}
REEEEE
REEEEE•3mo ago
Does what I sent not work?
Mr Void
Mr VoidOP•3mo ago
no sir; Property 'find' does not exist on type 'FlashcardCollection'. The following error is a conflict between FlashcardUpdate and FlashcardCollection
Argument of type 'FlashcardUpdate' is not assignable to parameter of type '{ author_id: string; created_at: string; id: string; modified_at: string; title: string; } & { flashcards: Flashcard[]; shareId: string | undefined; can_edit: boolean; shared_with: string | undefined; collaborators: Collaborator[]; }'.
Property 'title' is missing in type 'FlashcardUpdate' but required in type '{ author_id: string; created_at: string; id: string; modified_at: string; title: string; }'
Argument of type 'FlashcardUpdate' is not assignable to parameter of type '{ author_id: string; created_at: string; id: string; modified_at: string; title: string; } & { flashcards: Flashcard[]; shareId: string | undefined; can_edit: boolean; shared_with: string | undefined; collaborators: Collaborator[]; }'.
Property 'title' is missing in type 'FlashcardUpdate' but required in type '{ author_id: string; created_at: string; id: string; modified_at: string; title: string; }'
the way i setup the store is a bit awkward: store.tsx
const [state, setState]: [Store<StoreState>, SetStoreFunction<StoreState>]
= createStore<StoreState>({} as StoreState)

setState({
// ..
flashcards: flashcardStore(state, setState),
})
const [state, setState]: [Store<StoreState>, SetStoreFunction<StoreState>]
= createStore<StoreState>({} as StoreState)

setState({
// ..
flashcards: flashcardStore(state, setState),
})
flashcardStore.ts
export type FlashcardCollection = FlashcardCollectionRow & {
flashcards: Flashcard[]
// ...
}

export type FlashcardStore = {
collections: FlashcardCollection[]
// ...
}
export type FlashcardCollection = FlashcardCollectionRow & {
flashcards: Flashcard[]
// ...
}

export type FlashcardStore = {
collections: FlashcardCollection[]
// ...
}
path to a single flashcard is: store.flashcards.collections[ i ].flashcards[ j ]
REEEEE
REEEEE•3mo ago
You could use produce and instead of doing the map calls like in your original post, just do the Object assign After finding the card with find or a normal loop
Mr Void
Mr VoidOP•3mo ago
I don't understand what you mean could you please write a short example @REEEEE Think it's a good idea to apply states to the cards as in the following example? https://www.solidjs.com/tutorial/stores_nested_reactivity?solved perhaps 🤔 then, It would be simpler to manage the state of each? Tried doing it this way, assigning state getter/setter to the card objects within store. It elimiates the need to iterate over collections and cards, and doesn't cause re-rendering for other card objects 🤔
REEEEE
REEEEE•3mo ago
Sorry for the late response, this is what I meant
updateCard(flashcard: Flashcard) {
setState("flashcards", "collections", produce((collections: FlashcardCollection[]) => {
const tempCollections = [...collections]
let cardToUpdate

for(const collection of collections){
cardToUpdate = collection.flashcards.find((c) => c.id === flashcard.id)

if(cardToUpdate !== undefined) break;
}

if(cardToUpdate){
Object.assign({}, cardToUpdate, flashcard)
}


)
})
}
updateCard(flashcard: Flashcard) {
setState("flashcards", "collections", produce((collections: FlashcardCollection[]) => {
const tempCollections = [...collections]
let cardToUpdate

for(const collection of collections){
cardToUpdate = collection.flashcards.find((c) => c.id === flashcard.id)

if(cardToUpdate !== undefined) break;
}

if(cardToUpdate){
Object.assign({}, cardToUpdate, flashcard)
}


)
})
}
Not sure if this will work exactly but it's the general idea
Mr Void
Mr VoidOP•3mo ago
I've tried this now, but it fails to render based on the changes. I've come to a point where I need to update multiple properties at once and using signals for each property seems like a bad-practice 🤔
Want results from more Discord servers?
Add your server