Good practices in SolidJS

Are there any tutorials which go in depth of what are good and bad practices when building apps with SolidJS? As someone who has no prior experience with reactive libraries I sometimes have hard time understanding if what I'm doing is good or bad practice and maybe sometimes I might be reinventing the wheel or not using SolidJS as intended. So, if there are any tutorials you could recommend on how to properly use SolidJS please let me know. Or just share general recommendations, really want to learn how to use this tool properly. Thanks!
3 Replies
bigmistqke
bigmistqke15mo ago
i don't know too much about tutorials, besides the ones from the docs, but regarding best practices: it's a bit less important in solid then in react imo. In solid the naive approach is often perfectly fine. If you want to refactor it's more for ease of development then for performance reasons. There are still gotchas, like in every framework I guess. Of the top of my head: - don't read and write to the same signal in an effect: will create an endless loop. so don't do createEffect(() => setCount(count() + 1) but instead write createEffect(() => setCount(count => count + 1) - preferably don't write to signals in effects at all: derived signals (p.ex const double = () => count() * 2) or memos when the computation is expensive (const memo = createMemo(() => count() * 2)) will be more performant then createEffect(() => setDouble(count() * 2)). - using 'singletons' (a signal not inside a component, but imported/exported from a js-module) is perfectly fine when doing client side rendering, but when doing ssr it is better to import them through context as not to accidentally create shared state on the server (that can also cause potential hydration issues) - don't destructure props and understand why. Getters/setters are used in props to access signals without having to call the function:
const [signal, setSignal] = createSignal("hello");
const obj = { get signal(){ return signal() }};
createEffect(() => console.log(obj.signal));
setTimeout(setSignal("world"), 1000)
const [signal, setSignal] = createSignal("hello");
const obj = { get signal(){ return signal() }};
createEffect(() => console.log(obj.signal));
setTimeout(setSignal("world"), 1000)
will log hello and then after a second world
const [signal, setSignal] = createSignal("hello");
const obj = { get signal(){ return signal() }};
const {signal as _signal} = obj
createEffect(() => console.log(_signal));
setTimeout(setSignal("world"), 1000)
const [signal, setSignal] = createSignal("hello");
const obj = { get signal(){ return signal() }};
const {signal as _signal} = obj
createEffect(() => console.log(_signal));
setTimeout(setSignal("world"), 1000)
will only log hello since you did accessed the signal outside a reactive context. the _signal logged inside the effect was the value hello. - last rule also applies to proxies/createStore/createMutable: accessing a property === secretly calling a signal. something to be mindful off. - instead of destructuring, use mergeProps and splitProps (I like to use defaultProps too from this issue: https://github.com/solidjs/solid/issues/1526) - use control flow components like <For/>, <Index/> and <Show/> instead of .map and ternary operators so you don't create unnecessary dom-elements (in solid <div/> creates an actual dom-element, so if u would use map and update the array a lot you would create/remove a lot of dom-elements).
elite174
elite17415mo ago
I'd add one more thing (not really obvious): use untrack (or on) when calling callbacks from props for your component for instance:
const Component = (props) => {
const [value, setValue] = createSignal(1);

// this is REALLY dangerous
// because onValueChange may use other signals
// so that means that this effect will subscribe
// on that signals too!
//createEffect(()=>{
// props.onValueChange(value())
//});

// instead use this:
createEffect(on(value, currentValue => props.onValueChange(currentValue)))

// or hacky way (prev is better)
createEffect(()=>{
// subscribe
value();

untrack(() => props.onValueChange(value()))
})


return <button type="button" onClick={()=>setValue(v => v+1)}>{value()}</button>
}
const Component = (props) => {
const [value, setValue] = createSignal(1);

// this is REALLY dangerous
// because onValueChange may use other signals
// so that means that this effect will subscribe
// on that signals too!
//createEffect(()=>{
// props.onValueChange(value())
//});

// instead use this:
createEffect(on(value, currentValue => props.onValueChange(currentValue)))

// or hacky way (prev is better)
createEffect(()=>{
// subscribe
value();

untrack(() => props.onValueChange(value()))
})


return <button type="button" onClick={()=>setValue(v => v+1)}>{value()}</button>
}
elite174
elite17415mo ago
If you're a lib author keep this in mind and provide "sanitized" functions to the user (these functions use untrack inside) which may be used inside the effect. Example: https://github.com/elite174/solid-undo-redo/blob/master/src/lib/travel.ts#L218
GitHub
solid-undo-redo/src/lib/travel.ts at master · elite174/solid-undo-r...
A list-based (O(1)) implementation of undo-redo for signals! - elite174/solid-undo-redo
Want results from more Discord servers?
Add your server