S
SolidJS•15mo ago
lars

Losing reactivity in props passed to `children` callback

I've got a component Field that passes a memo to props.children. I'm running into an issue where the child component is not updating on changes to the memo
export function Field(props: { form: Form; children: JSXElement }) { // `Form` is a SolidJS store
const fieldState = createMemo(() => {
return {
value: store.value,
isDisabled: store.isDisabled,
isRequired: store.isRequired,
hasError: store.hasError,
};
});

createEffect(() => console.log('Changed', fieldState()); // Is logging successfully on changes

return props.children(fieldState());
}

// ---------------------------
<Field form={myForm}>
{(fieldState) => (
<>
<p>{JSON.stringify(fieldState)}</p> // !! Not updating on changes
<CustomInput {...fieldState} />
</>
)}
</Field>
export function Field(props: { form: Form; children: JSXElement }) { // `Form` is a SolidJS store
const fieldState = createMemo(() => {
return {
value: store.value,
isDisabled: store.isDisabled,
isRequired: store.isRequired,
hasError: store.hasError,
};
});

createEffect(() => console.log('Changed', fieldState()); // Is logging successfully on changes

return props.children(fieldState());
}

// ---------------------------
<Field form={myForm}>
{(fieldState) => (
<>
<p>{JSON.stringify(fieldState)}</p> // !! Not updating on changes
<CustomInput {...fieldState} />
</>
)}
</Field>
It works totally fine if I instead have several memos though:
export function Field(props: { form: Form; children: JSXElement }) {
const fieldState = {
value: createMemo(() => store.value),
isDisabled: createMemo(() => store.isDisabled),
...
};

return props.children(fieldState);
}

// ---------------------------
<Field form={myForm}>
{(fieldState) => (
<>
<p>{JSON.stringify(fieldState)}</p> // Updating correctly now
<CustomInput value={fieldState.value()} isDisabled={fieldState.isDisabled()} ... />
</>
)}
</Field>
export function Field(props: { form: Form; children: JSXElement }) {
const fieldState = {
value: createMemo(() => store.value),
isDisabled: createMemo(() => store.isDisabled),
...
};

return props.children(fieldState);
}

// ---------------------------
<Field form={myForm}>
{(fieldState) => (
<>
<p>{JSON.stringify(fieldState)}</p> // Updating correctly now
<CustomInput value={fieldState.value()} isDisabled={fieldState.isDisabled()} ... />
</>
)}
</Field>
a downside with the working example is having to pass in all props explicitly instead of just spreading em (ie. {...fieldState}) 😬 how come the reactivity is lost? is there a way to preserve reactivity + still being able to spread the props?
1 Reply
lars
lars•15mo ago
solved by passing the memo to props.children without actually invoking the memo
export interface ParentProps {
children: (cb: { someDerivedProperty: string }) => void;
}

export function Parent(props: ParentProps) {
...
return props.children(myMemo); // not invoking the memo this time
}

<Parent myStore={store}>
{(props) => (
<Child {...props()} />
)}
</Parent>
export interface ParentProps {
children: (cb: { someDerivedProperty: string }) => void;
}

export function Parent(props: ParentProps) {
...
return props.children(myMemo); // not invoking the memo this time
}

<Parent myStore={store}>
{(props) => (
<Child {...props()} />
)}
</Parent>
Want results from more Discord servers?
Add your server