S
SolidJS12mo ago
Mr Void

component doesnt react to changes in state

I've got a component that has a state collaborators which holds an array of objects. The problem is that when this state is modified, through setCollaborators function, the component does not update for the new value. why is this happenin, and how can I work around this issue?
const CollectionsView = () => {
const store: StoreState = useStore()
const params = useParams()

const [currentCollection, setCurrentCollection] = createSignal<Collection>()
const [currentCollaborators, setcurrentCollaborators] = createSignal<UserSummary[]>([])


createEffect(() => {
console.log(store.cards.collections)

let collection: Collection | undefined
= store.cards.collections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
collection = store.cards.sharedCollections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
navigate("/fcards")
return
}
}

batch(() => {
setCurrentCollection(collection)
setcurrentCollaborators(collection!.collaborators)
// this shows the correct value:
console.log("collaborators: ", currentCollaborators())
})
})

return (
// ...
// the following does not re-render with new changes
<For each={currentCollaborators()!}>
{(collaborator) => <div>{collaborator.display_name}</div>}
</For>
)
const CollectionsView = () => {
const store: StoreState = useStore()
const params = useParams()

const [currentCollection, setCurrentCollection] = createSignal<Collection>()
const [currentCollaborators, setcurrentCollaborators] = createSignal<UserSummary[]>([])


createEffect(() => {
console.log(store.cards.collections)

let collection: Collection | undefined
= store.cards.collections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
collection = store.cards.sharedCollections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
navigate("/fcards")
return
}
}

batch(() => {
setCurrentCollection(collection)
setcurrentCollaborators(collection!.collaborators)
// this shows the correct value:
console.log("collaborators: ", currentCollaborators())
})
})

return (
// ...
// the following does not re-render with new changes
<For each={currentCollaborators()!}>
{(collaborator) => <div>{collaborator.display_name}</div>}
</For>
)
29 Replies
Brendonovich
Brendonovich12mo ago
My guess is that the values inside collection.collaborators are updating, but the array itself isn't changing - signals only cause updates when their whole value changes. setcurrentCollaborators([...collection!.collaborators]) might do the trick. Though with that said, I don't think this is a good use of signals and effects. createMemo would be a better fit here, since all you're really doing here is deriving currentCollection and currentCollaborators from your store and route params. That would eliminate any signal update problems anyway since you'd be referencing the original store.
Mr Void
Mr VoidOP12mo ago
spreading the values have not helped either 🤔 I dont understand why it doesnt react to the change. In what way would createMemo help with this issue? Could you please show an example I noticed something... the state "currentCollaborators" is not changing, but the store itself does change 🤔 For some reason the state's value isnt changing 😩
Brendonovich
Brendonovich12mo ago
Memos for this case are just more in-line with how Solid works, since currentCollection is just derived from your store + router state. There’s plenty of examples out there on using memos, I can’t provide any rn
Mr Void
Mr VoidOP12mo ago
After using:
const currentCollaborators = createMemo(() => currentCollection()?.collaborators)
const currentCollaborators = createMemo(() => currentCollection()?.collaborators)
The value does change, however the JSX component doesnt re-render PepeHands
REEEEE
REEEEE12mo ago
const currentCollaborators = createMemo(() => [...currentCollection()?.collaborators]) maybe?
Mr Void
Mr VoidOP12mo ago
this didn't help either, doesn't update at all now pepeKMS
Brendonovich
Brendonovich12mo ago
Did you make currentCollection a memo as well?
Mr Void
Mr VoidOP12mo ago
no, that one is in effect atm I'll try memo, one moment 😩 no luck:
const currentCollection = createMemo((prev) => {
const collection: FlashcardCollection | undefined
= store.flashcards.collections.find(
(collection) => collection.id === params.collection_id
) || store.flashcards.sharedCollections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
navigate("/flashcards")
return prev
}

return collection
})

const currentCollaborators = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
if (coll === undefined) {
return []
}

return [...coll.collaborators]
})

createEffect(() => {
if (currentCollection()) {
setLoading(false)
}
})
const currentCollection = createMemo((prev) => {
const collection: FlashcardCollection | undefined
= store.flashcards.collections.find(
(collection) => collection.id === params.collection_id
) || store.flashcards.sharedCollections.find(
(collection) => collection.id === params.collection_id
)

if (collection === undefined) {
navigate("/flashcards")
return prev
}

return collection
})

const currentCollaborators = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
if (coll === undefined) {
return []
}

return [...coll.collaborators]
})

createEffect(() => {
if (currentCollection()) {
setLoading(false)
}
})
REEEEE
REEEEE12mo ago
weird weird, does the value of currentCollaborators change as expected?
Mr Void
Mr VoidOP12mo ago
no it remains same length Could it have to do with deep objects in store?
REEEEE
REEEEE12mo ago
maybe? Does the collection get modified after params is changed? you could try to unwrap on the collection return in currentCollection return unwrap(collection)
Mr Void
Mr VoidOP12mo ago
what is the purpose of unwrap
REEEEE
REEEEE12mo ago
to remove the proxy on store objects and give you the raw value
Mr Void
Mr VoidOP12mo ago
this works, but why
REEEEE
REEEEE12mo ago
My guess is that something is changing the collection after this code runs causing the coll.collaborators to be changed. Since currentCollection returns an store proxy of an object in the store, accessing a property inside that object causes currentCollaborators to track that nested property for any changes. I think an alternative is to use untrack
Mr Void
Mr VoidOP12mo ago
this is confusing, what is proxy? This did work with other parts of store, when for example creating a signal and having it's value retrieved from store
REEEEE
REEEEE12mo ago
const currentCollaborators = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
return untrack(() => {
if (coll === undefined) {
return []
}

return [...coll.collaborators]
})
})
const currentCollaborators = createMemo(() => {
const coll = currentCollection() as FlashcardCollection
return untrack(() => {
if (coll === undefined) {
return []
}

return [...coll.collaborators]
})
})
A proxy basically intercepts read/write to an object. Stores use proxies to help with creating their nested reactivity by creating a signal for a property when accessed. For example, if I do store.firstName somewhere in my code for the first time, the store will create a signal for that firstName property and call it's getter to give you the value. So if you were using this in an effect:
createEffect(() => {
store.firstName
})
createEffect(() => {
store.firstName
})
the effect would now be tracking firstName. For each level down you go (store.flashcards.collections), it is a proxy that connects to a signal. So accessing that property in an effect will subscribe that effect to the property. In your case, collection is an object in either store.flashcards.collections or store.flashcards.sharedCollections. collection.collaborators will be subscribed to in the memo because collaborators is a proxy connected to a signal. I hope this makes some sense. I'm not the best at explaining the core concepts, but hope it helps
Mr Void
Mr VoidOP12mo ago
I understand what you mean, Im somewhat familiar with the fact that createEffect listens to some dependency like store.flashcards.collections. what confuses me is whether having called .find(...) to retrieve the desired collection broken some part of that reactivity tracking mechanism. Does the dependency have to be accessed directly as a.b.c.d rather than d = a.b.c.find(...) in order for the component to notice changes? 🤔
REEEEE
REEEEE12mo ago
I think .find(...) should still track because it accesses the values inside itself like when you do collection.id === params.collection_id, id of each collection in store.flashcards.collections is being tracked. It's hard to say without looking at the code, but there might be something that was modifying the collection's collaborators somewhere
Mr Void
Mr VoidOP12mo ago
collaborators are only updated in store's setState call, where some new collaborator object is added. ( each property is destructured all the way to the collaborators list ), would you like to see
REEEEE
REEEEE12mo ago
sure
Mr Void
Mr VoidOP12mo ago
Oki one moment
setState("flashcards", collectionType, (collections: FlashcardCollection[]) =>
{
const sa = [...collections].map(collection => {
if (collection.id !== collection_id) {
return collection
}

const tempCollection = {...collection}
tempCollection.collaborators.push(collaborator)
return tempCollection
})

return sa
}
)
setState("flashcards", collectionType, (collections: FlashcardCollection[]) =>
{
const sa = [...collections].map(collection => {
if (collection.id !== collection_id) {
return collection
}

const tempCollection = {...collection}
tempCollection.collaborators.push(collaborator)
return tempCollection
})

return sa
}
)
this is the only part that modifies collaborators array
REEEEE
REEEEE12mo ago
when does this get run? Also, when you said it works, does it update the JSX too?
Mr Void
Mr VoidOP12mo ago
yes it finally updates the component PepeHands new values are being rendered on screen as expected it is executed when a collaborator is added to the collection, an event fires and calls store's function to update collaborators button add collaborator -> event -> store.addCollaborator(...) progress nonethe less peporestless thank you a lot sir Reeeee 😁
REEEEE
REEEEE12mo ago
peeposmile Nice
Mr Void
Mr VoidOP12mo ago
it is time to lay down, brain is fried PepeHands short circuit
REEEEE
REEEEE12mo ago
I couldn't explain the issue completely but glad it works 🫡 take a break
Mr Void
Mr VoidOP12mo ago
It is alright, you have given me enough insight to continue working on it further and read some more about the magic of solid thank you veri much peposmile
REEEEE
REEEEE12mo ago
pepeOK
Want results from more Discord servers?
Add your server