S
SolidJS8mo ago
edygar

createSignal + createRenderEffect

Say I have an Input component that might or not be controlled. Within this said component, I need to peek reactively into the value. Is it a good idea to have something like the following?
function Input(props) {
const [value, setValue] = createSignal<Exclude<typeof props["value"], undefined>>()
createRenderEffect(() => {
setValue(props.value)
})

createEffect(() => {
doClientSideOnlyStuff(value())
})

return (
<input
// ... many other stuff
value={value()}
onInput={(e) => {
const previous = value()
// expose normally
props.onInput?.(e)

if (!e.defaultPrevented && value() === previous && previous !== e.target.value) {
// this order matter, first locally
setValue(e.target.value)
}
}}
/>
)
}
function Input(props) {
const [value, setValue] = createSignal<Exclude<typeof props["value"], undefined>>()
createRenderEffect(() => {
setValue(props.value)
})

createEffect(() => {
doClientSideOnlyStuff(value())
})

return (
<input
// ... many other stuff
value={value()}
onInput={(e) => {
const previous = value()
// expose normally
props.onInput?.(e)

if (!e.defaultPrevented && value() === previous && previous !== e.target.value) {
// this order matter, first locally
setValue(e.target.value)
}
}}
/>
)
}
This is also compatible with SSR, since the createRenderEffect will run on the server. My main doubt is for things like
6 Replies
bigmistqke
bigmistqke7mo ago
value() === previous this line confuses me a bit. isn't it always going to be true?
edygar
edygarOP7mo ago
@bigmistqke previous hold the value before onInput, that might update externally the props.value which in turn would trigger the render effect, resulting in a new state. in this case, if the value is already updated, there's nothing to be done. Imagine props.onInput derives the value form e.target.value but changing it (eg. input mask). But now, explaining it to you, I realise that props.value is always up-to-date upon reading, instead of holding previous I could just check whether props.value is undefined, if so, I could call the setValue That's to cover the case where the consumer doesn't set the value
function Input(props) {
const [value, setValue] = createSignal<Exclude<typeof props["value"], undefined>>()
createRenderEffect(() => {
setValue(props.value)
})

createEffect(() => {
doClientSideOnlyStuff(value())
})

return (
<input
// ... many other stuff
value={value()}
onInput={(e) => {
props.onInput?.(e)

if (!e.defaultPrevented && props.value === undefined) {
// in case consumer never defined the props.value
// this maintains value always up-to-date
setValue(e.target.value)
}
}}
/>
)
}
function Input(props) {
const [value, setValue] = createSignal<Exclude<typeof props["value"], undefined>>()
createRenderEffect(() => {
setValue(props.value)
})

createEffect(() => {
doClientSideOnlyStuff(value())
})

return (
<input
// ... many other stuff
value={value()}
onInput={(e) => {
props.onInput?.(e)

if (!e.defaultPrevented && props.value === undefined) {
// in case consumer never defined the props.value
// this maintains value always up-to-date
setValue(e.target.value)
}
}}
/>
)
}
bigmistqke
bigmistqke7mo ago
so if u set the prop on the input u have to update signals from the consuming component?
edygar
edygarOP7mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
edygar
edygarOP7mo ago
console.log is ran for both sourcing from internal signal with the most up to date value
bigmistqke
bigmistqke7mo ago
gotcha. i think i missed this Say I have an Input component that might or not be controlled part of ur question. the logic of combining both confused me but now i understand the goal
Want results from more Discord servers?
Add your server