S
SolidJS14mo ago
alloyed

encapsulating logic that uses refs?

Hi, so I'm porting a react app that does something like so (eliding lots of annoying extra details):
function useMyCustomHook(ref)
{
useEffect(()=>{
if(ref.current)
{
doSomethingWithRef(ref.current);
return ()=> {cleanup(ref.current);}
}
return ()=> {doNothing();}
}, [])
}

function MyComponent()
{
const ref: React.RefObject<T> = ...;
useMyCustomHook(ref);
return <elment ref={ref} />
}
function useMyCustomHook(ref)
{
useEffect(()=>{
if(ref.current)
{
doSomethingWithRef(ref.current);
return ()=> {cleanup(ref.current);}
}
return ()=> {doNothing();}
}, [])
}

function MyComponent()
{
const ref: React.RefObject<T> = ...;
useMyCustomHook(ref);
return <elment ref={ref} />
}
There's a problem with the naive translation of this:
function createCustomEffect(refB)
{
createEffect(()=>{
doSomethingWithRef(refB);
onCleanup(()=> {cleanup(refB));
});
}

function MyComponent()
{
let refA: T = ...;
createCustomEffect(refA);
return <elment ref={refA} />
}
function createCustomEffect(refB)
{
createEffect(()=>{
doSomethingWithRef(refB);
onCleanup(()=> {cleanup(refB));
});
}

function MyComponent()
{
let refA: T = ...;
createCustomEffect(refA);
return <elment ref={refA} />
}
which is that unlike react, which will rerun the hook if ref changes, and normally only changes ref.current to prevent this, solid will run the effect one with the initial value, and completely not notice that the refA changed. because of pass-by-value, refB is a copy of the original binding to the original value, not a reference to the refA binding. what alternate patterns should I be trying instead? some off-the-cuff thoughts: * put the ref into a signal ala ref={setSignal} * split out the doSomething() parts of the hook and the createEffect() part of the hook. In this world by convention you aren't actually allowed to encapsulate createEffect() calls, only the contents of them (with any dependency injection you need to pass in signals or w/e) * something else? bring back ref.current?
4 Replies
REEEEE
REEEEE14mo ago
Use a signal for a ref And pass the signal to the custom hook createCustomEffect(signalRef) Then in the hook, call refB Inside the effect
aryzing
aryzing14mo ago
👍
alloyed
alloyedOP14mo ago
yep, that worked! what I ended up doing ultimately was
// change
function createCustomEffect(refB: T)
{
...
}
// to
function createCustomEffect(refB: Accessor<T>)
{
...
}

// and in the containing component:
let refA!: T;
createCustomEffect(()=>refA);
// change
function createCustomEffect(refB: T)
{
...
}
// to
function createCustomEffect(refB: Accessor<T>)
{
...
}

// and in the containing component:
let refA!: T;
createCustomEffect(()=>refA);
doing the inline callback thing vs making a full signal is just a matter of preference I think, the signal itself won't ever change after initial mount
REEEEE
REEEEE14mo ago
There could be cases where it does, for example, if you have a component with the ref in a Show the ref could change but in this case your callback wouldn't be reactive to refA changing so it would be pointing to something that no longer exists
Want results from more Discord servers?
Add your server