Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

isReactive #8

Open
titoBouzout opened this issue Nov 1, 2023 · 4 comments
Open

isReactive #8

titoBouzout opened this issue Nov 1, 2023 · 4 comments

Comments

@titoBouzout
Copy link

Hello,

I made a reactive renderer using solid reactivity, see https://pota.quack.uy/ , so I have some feedback and suggestions.

There's a need to know if something is a signal/memo

I propose a isReactive which returns true when something is a signal or a memo. The terminology isReactive because is ambiguous enough, it is not like it matters if it is a signal or a memo specifically, and avoids the case for when there's a need to distinguish a signal from a memo, in case isSignal and isMemo becomes a thing for reasons

When isReactive not needed

Contrary to popular belief, an isReactive is not helpful for this case:

function SomeComponent(props) {
  createEffect(() => {
   document.title = isReactive(props.title) ? props.title() : props.title
  })
}

Because it doesn't change anything to know if prop.title is a signal or not. prop.title could be

prop.title = function(){
  return signalSiteName() + ' - ' + signalSiteCategory() + ' - ' + signalPageTitle()
}

Then, what one would do when expecting a maybe signal is the following:

function SomeComponent(props) {
  createEffect(() => {
   document.title = getValue(props.title)
  })
}

export const getValue = value =>
  typeof value === 'function' ? getValue(value()) : value

When is useful to know if thing is isReactive

When processing a tree, you find a component with some callbacks, and there's no way to know what should be tracked and what should be not tracked.

<Show when={true}>
{
  [
    (item) => {
     // this is untracked
    } ,
    signal // this should be tracked
  ]
}
</Show>

One could probably just wrap the signal/memo https://github.com/potaorg/pota/blob/master/src/lib/reactivity/primitives/solid.js#L37-L50, but brings these questions:

  1. is this the performant way to mark the signal/memo? how to do it?
  2. everyone will be doing the same thing if they have a use for knowing if something is a signal/memo?

Functions to effects

If a signal/memo has any meaning to you, then you would do something like the following, because you would want to use the reactive property of the function you are receiving

if (isReactive(unknown)) {
  const placeholder = givemePlaceholder();
  createEffect(() => {
    placeholder.clear();
    placeholder.append(unknown());
  });
} else {
  body.append(unknown());
}
@lxsmnsyc
Copy link
Member

lxsmnsyc commented Nov 2, 2023

This is borderline impossible to implement.

@fabiospampinato
Copy link
Collaborator

fabiospampinato commented Nov 2, 2023

I think the assumption is that this would be useful for use cases such as @titoBouzout's, where getters are not used so props.foo is just whatever was passed to it, which makes implementing this trivial.

@ryansolid
Copy link
Member

I still don't get what it solves. Any function could need to be tracked. Just because a subset is a Signal or Memo doesn't change that.

@titoBouzout
Copy link
Author

titoBouzout commented Mar 10, 2024

Say you have

function Counter() {
  const [count, setCount] = createSignal(1);
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}

<Counter/>

vs

const [Counter, setCount] = createSignal(1);
<Counter/>

Say you have

function Counter() {
  const [count, setCount] = createSignal(1);
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}

function insert(thing, where){
    where.append(makeComponent(thing))
}
function makeComponent(thing){
    return untrack(thing)
}

insert(Counter, document.body)

vs

const [Counter, setCount] = createSignal(1);

function insert(thing, where){
    where.append(makeComponent(thing))
}
function makeComponent(thing){
    return untrack(thing)
}

insert(Counter, document.body)

function Counter() {
  const [count, setCount] = createSignal(1);
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}
const [Count, setCount] = createSignal(1);

<Show when={{someValue:true}}>
{Counter}
{Count}
</Show>

 
const writable = createWritableMemo(1);

<Show when={{someValue:true}}>
{writable}
</Show>
// writable === {someValue:true}

I haven't use writable memos much yet, but I can already see that isReadable and isWritable would be also useful. You have a callback that receives a value, you do not want to give that callback the value when it's a writable because it will change its value (case for when same function acts as getter/setter). Sometimes you sort of cant avoid this. like a ref. It provides a nice DX by hiding in plain sight the fact that you can write and read to the thing at the same time.

import {ref} from 'pota'

const element = ref() // readable and writable signal 

effect(()=>{
   if(element()){ ... }
})

<input ref={element}/>

addendum

The following irrelevant(for the case) example always comes up:

  createEffect(() => {
   document.title = isReactive(props.title) ? props.title() : props.title
  })

Some items above I mention thats solved instead as

createEffect(() => {
   document.title = getValue(props.title)
  })

export const getValue = value =>
  typeof value === 'function' ? getValue(value()) : value

Since then, I have found a pattern that meets better my expectations

withValue(props.title, (value) =>  document.title = value)

const withValue = (value, fn) =>
	typeof value  === 'function'
		? effect(() =>  fn(value()))
		: fn(value)

As you can see on this block of addendum text, knowing if something is a signal or not its completely irrelevant.


All of this only makes sense if you think of solidjs/signals as an API that a consumer could just take and consume, as they wish, with their own requirements and idiosyncrasies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants