S
SolidJS•15mo ago
Mathieu

Make signal react on prop change

Say you initialize a signal by a prop:
const [error, setError] = createSignal(props.error);
const [error, setError] = createSignal(props.error);
If the prop changes, the signal won't react. What must be written is:
const [error, setError] = createSignal(props.error);
createEffect(() => setError(props.error));
const [error, setError] = createSignal(props.error);
createEffect(() => setError(props.error));
Wonder if there's a more idiomatic way in SolidJS though 🤔
12 Replies
thetarnav
thetarnav•15mo ago
Solid Primitives
A library of high-quality primitives that extend SolidJS reactivity
JCM
JCM•15mo ago
To state the obvious: The idiomatic way to use a prop is to use the prop directly 😀 . The idiomatic way to have a derived value from one or more props is to define a function using the props (or use a memo). Now it's unlikely this answers your questions, however without background of what you want to achieve, it's hard to suggest an idiomatic way to do things. The problem of your example or the writable memo is that it's kind of a derived value, but not really. If something else writes, it will be something else, until the prop changes. The latest write wins. There are certainly use cases for this, but I would look for alternatives first, as it has the potential for surprising behavior.
high1
high1•15mo ago
There's nothing wrong without updating the state in effect, it's an awkward case though - does the component depend on it's internal state or on props?
Mathieu
Mathieu•15mo ago
@jc_m @.high1 so reading your comments, the case must clarified, as it might be unusual or weird. I have custom form input components, say a TextField (among others). They maintain their own state for errors. For example, I can give it a minLength attribute and it display an error message if the length inputted by the user is below the requirement. So I initially had:
const [error, setError] = createSignal('');
// ...
setError(translate('invalid_min_length', { minLength: props.minLength }));
const [error, setError] = createSignal('');
// ...
setError(translate('invalid_min_length', { minLength: props.minLength }));
All handled in the component itself. BUT, there are some errors that can't be handled by the component. For example errors that can only be known after a server request. Typical example would be "email already used" in a registration form (I need to hit the db in order to know). So I allow the TextField to be given an external error message, through props, so now I have:
const [error, setError] = createSignal('');
createEffect(() => setError(props.error));
const [error, setError] = createSignal('');
createEffect(() => setError(props.error));
But I wonder, maybe this has some complicated effects, where the parent doesn't have an error anymore (empty string), but there was some internal error, and the parent may reset some internal error, not sure. Actually I'm confused now. With tests this works perfectly though as I have written above.
high1
high1•15mo ago
The code will work, that's not the issue. The issue here is the component mental model, I would say.
REEEEE
REEEEE•15mo ago
You could also create a derived signal based on both the internal signal value and the prop value.
const [_error, setError] = createSignal('')

const error = () => {
return props.error ? props.error : _error()
}
const [_error, setError] = createSignal('')

const error = () => {
return props.error ? props.error : _error()
}
high1
high1•15mo ago
You have an internal state for which is initialized via props - if that prop updates, the state - signal - will not be updated without the effect. But what's the source of the truth - the prop or the internal state? If the internal stage changed, and then the prop changes - what then? Which one overrides the other? I always felt like I'm not doing things completely clean when I needed to do this.
Mathieu
Mathieu•15mo ago
thanks for all the input guys, you made it clear that I didn't reason enough about the case and I'll take some time to think about it. I think it's quite a common thing though, I'm surely not the first to encounter this need but I need to express my need more clearly, I didnt consider all the cases enough
high1
high1•15mo ago
Derived signal would be fine here I guess, as @._rb proposed.
Mathieu
Mathieu•15mo ago
and yeah, the question at the core is, what is my source of truth. excellent question
high1
high1•15mo ago
In this case, both are active - but separate - so there's no override
Mathieu
Mathieu•15mo ago
Btw ty @._rb my final code is
const [_error, setError] = createSignal('');
const error = () => props.error || _error();
const [_error, setError] = createSignal('');
const error = () => props.error || _error();
Want results from more Discord servers?
Add your server
More Posts