S
SolidJS•17mo ago
Kasper

Component is not updated when prop is changed

I have a component "ShowDocument" with a resource inside, the component has a variables that needs to used in the resources. The component is called with the following: <ShowDocument documentId={showDoc()} onClose={closePanel} /> my issues is that when signal showDoc() in the prop "documentId" is updated the component ShowDocument is not updated, and will continue to show what was there when it was first rendered.
const ShowDocument = (props: { documentId: string | undefined; onClose: () => void }) => {
console.log("ShowDocument: ", props.documentId);
const { getSecureDocumentLink } = useDocuments();

const [documentLink] = createResource(props.documentId, async (documentId) => {
console.log("Fetching new document link: ", documentId);
if (!documentId) {
return undefined;
}
console.log("Getting secure link for document: ", documentId);
let link = await getSecureDocumentLink(documentId, false);
console.log("Got secure link: ", link);
return link;
});

return (
<div class="flex flex-col h-full w-full">
<div>
<button onClick={props.onClose} class="bg-blue-500 rounded font-bold p-2 border-solid border-2 border-blue-600 hover:bg-blue-600">
Close
</button>
</div>
<div class="flex-1">
<Show when={documentLink() && !documentLink.loading} fallback={<span>Loading...</span>}>
<iframe src={documentLink()} class=" h-full w-full"></iframe>
</Show>
</div>
</div>
);
};
const ShowDocument = (props: { documentId: string | undefined; onClose: () => void }) => {
console.log("ShowDocument: ", props.documentId);
const { getSecureDocumentLink } = useDocuments();

const [documentLink] = createResource(props.documentId, async (documentId) => {
console.log("Fetching new document link: ", documentId);
if (!documentId) {
return undefined;
}
console.log("Getting secure link for document: ", documentId);
let link = await getSecureDocumentLink(documentId, false);
console.log("Got secure link: ", link);
return link;
});

return (
<div class="flex flex-col h-full w-full">
<div>
<button onClick={props.onClose} class="bg-blue-500 rounded font-bold p-2 border-solid border-2 border-blue-600 hover:bg-blue-600">
Close
</button>
</div>
<div class="flex-1">
<Show when={documentLink() && !documentLink.loading} fallback={<span>Loading...</span>}>
<iframe src={documentLink()} class=" h-full w-full"></iframe>
</Show>
</div>
</div>
);
};
14 Replies
Kasper
Kasper•17mo ago
From what i have been able to understand for my googleing is that SolidJS dose not rerender a component when the props change. So am i correct in assuming that i should give the documentId as an Accessor<string | undefined> instead? That works but is it the correct way of doing it?
REEEEE
REEEEE•17mo ago
The resource doesn't rerun i'm guessing ? If you do, () => props.documentId instead it should work
Kasper
Kasper•17mo ago
@._rb sorry for the late reply, what if i have a component without a resources. Such like the one below that is used multiple in places. Some place where the props are static and other where they are dynamic. such as:
interface CategorySelectorProps {
type?: CategoryType | undefined;
includeAllOption?: boolean;
leftExpand?: boolean;
value: Accessor<number | undefined>;
onChange: (value: number) => void;
}
const CategorySelector = ({ type, value, onChange, leftExpand, includeAllOption }: CategorySelectorProps) => {
const { categories, loading, categoryMap } = useCategories();

const [categoryName, setCategoryName] = createSignal<string | undefined>(undefined);
const [categoryParent, setCategoryParent] = createSignal<string | undefined>(undefined);
const [categoriesToDisplay, setCategoriesToDisplay] = createSignal<Category[]>([]);
const [showSelect, setShowSelect] = createSignal<boolean>(false);

createEffect(() => {
if (!categories() || categories().length === 0) {
setCategoriesToDisplay([]);
console.log("No categories to display");
return;
}

if (!type) {
setCategoriesToDisplay(categories());
console.log("No type specified, displaying all categories");
return;
}

console.log("Displaying categories of type: ", type);
let filteredCategories = categories().filter((c: Category) => c.type === type);
setCategoriesToDisplay(filteredCategories);
});
...
};
interface CategorySelectorProps {
type?: CategoryType | undefined;
includeAllOption?: boolean;
leftExpand?: boolean;
value: Accessor<number | undefined>;
onChange: (value: number) => void;
}
const CategorySelector = ({ type, value, onChange, leftExpand, includeAllOption }: CategorySelectorProps) => {
const { categories, loading, categoryMap } = useCategories();

const [categoryName, setCategoryName] = createSignal<string | undefined>(undefined);
const [categoryParent, setCategoryParent] = createSignal<string | undefined>(undefined);
const [categoriesToDisplay, setCategoriesToDisplay] = createSignal<Category[]>([]);
const [showSelect, setShowSelect] = createSignal<boolean>(false);

createEffect(() => {
if (!categories() || categories().length === 0) {
setCategoriesToDisplay([]);
console.log("No categories to display");
return;
}

if (!type) {
setCategoriesToDisplay(categories());
console.log("No type specified, displaying all categories");
return;
}

console.log("Displaying categories of type: ", type);
let filteredCategories = categories().filter((c: Category) => c.type === type);
setCategoriesToDisplay(filteredCategories);
});
...
};
At the dynamic location i create the componet like this: <CategorySelector leftExpand={true} type={type()} value={categoryId} onChange={setCategoryId} /> but when type() updates the component is not updated. Is the right approach here to send in type as the Accessor and not just as the value. and then when it is static i need to wrap my static field in a createSignal("my-type")[0]?
REEEEE
REEEEE•17mo ago
Don't destructure the props const CategorySelector = (props: CategorySelectorProps) then it should be fine
Kasper
Kasper•17mo ago
Great that worked. Why do destructing the props break that? Also is better practices to send the Accessor to sub-component?
REEEEE
REEEEE•17mo ago
Generally in Solid, never destructure props. Props are all turned into getter functions by Solid's transform so it causes them to lose their reactivity when destructured. I'm not too technical on this end so that's my basic knowledge. It's been discussed many times here before but I always forget the in depth explanation Accessor passing has also been discussed a bit and I can't remember the details Let me see if I can find something
Kasper
Kasper•17mo ago
Accessor passing should be fine as when you call the function on it subscribes it self to parent context of where it is used. Or at least that is how i understand it. My very basic understanding would say that passing a Accessor would cause less updates as only the parts of the subcomponent that is affected is updated. But i am not sure at all. And i really want to learn the best practices 🙂
REEEEE
REEEEE•17mo ago
https://discord.com/channels/722131463138705510/751355413701591120/1067550971707805758 This is where a recent discussion starts about this There are cases where you have to pass Accessor rather than the value itself but I'm not too familiar on the technical side of things so I will say it's preference I'll @ someone who will be able to explain it better Hey @foolswisdom, if you get a chance could you help explain this as I'm not able to https://discord.com/channels/722131463138705510/990790895244808192/1069988324946747503 Another explanation for the destructuring
foolswisdom
foolswisdom•17mo ago
Fabio's description (in the linked discussion) is good. There's also the presentation about this I gave at the NYC meetup. I posted a link a couple times, usually after Fabio gave his explanation
foolswisdom
foolswisdom•17mo ago
The key here is that props access is actually a function (getter) call, and using destructuring changes where that call happens. If it happens inside an effect / memo / jsx expression, it gets tracked, but it doesn't get tracked if it's called at the top level (because tracking the top level would mean we would rerun components, which we don't want to do). Destructuring is just a form of prop access, but if you do that in the function definition, then that's top level access and not tracked Thanks for finding it Sorry, I'm rather tired and heading to bed soon, hopefully this is sufficiently clear
REEEEE
REEEEE•17mo ago
Thanks a bunch, sorry for the trouble! I'll try to keep this in mind so I can explain it as well
foolswisdom
foolswisdom•17mo ago
You could pass an accessor of course, but the final behavior in terms of scope rerunning will be the same. I cover the reasons we prefer a "pass by value" in the talk linked by @._rb The key is that our pass by value is actually passing function hidden as a getter, so you get similar functionality, but with a nicer DX (though of course Fabio disagrees about the DX bit, he's the man for spicy opinions 😉) Thanks for tagging me 🙂 Tagging people is how we spread the knowledge 😉
Kasper
Kasper•17mo ago
Thank you very much! Really loving SolidJS 🙂
Want results from more Discord servers?
Add your server
More Posts