0

Props are what we call the object that is passed to our component function on execution that represents all the attributes bound to its JSX. Props objects are readonly and have reactive properties which are wrapped in Object getters. This allows them to have a consistent form regardless of whether the caller used signals, signal expressions, or static values. You access them by props.propName.

For this reason it is also very important to not just destructure props objects, as that would lose reactivity if not done within a tracking scope. In general accessing properties on the props object outside of Solid's primitives or JSX can lose reactivity. This applies not just to destructuring, but also to spreads and functions like Object.assign.

I found it https://www.solidjs.com/tutorial/props_defaults during the solid js tutorial. But I still don't understand how the destructuring of props cause a loss of reactivity.

 const { name } = props;
 return <div>{name}</div>
 return <div>{props.name}</div>

I don't know what differences there are between these. I think I don't understand yet how the reactivity works in SolidJS.

Do they do something more for components functions and JSX?

seongkuk han
  • 570
  • 6
  • 14
  • Yes, all JSX is implicitly wrapped inside an effect, and accessing a reactive getter property inside the effect works differently than accessing it outside – Bergi May 17 '23 at 13:56

3 Answers3

3

Because when destructured, a reactive value is extracted into regular static variables. Those values are not updated then on.

const [user, setUser] = createSignal({ name: 'John Doe' });

// Now name is a static variable
const { name } = user();

By the way, props are not plain objects but getter methods for passed properties, so they use implicit function invocation and that is how they remain reactive.

If you inspect the compiled code:

<Display user={user()} />

You will see it compiles to this:

_$createComponent(Display, {
  get user() {
    return user();
  }
});

You may ask how compiler knows if it is a signal, it does not. This works for all function invocations.

If you need to opt out of this, place the once comment in front of the signals:

<Display user={/*@once*/user()} />

Now value will be static:

_$createComponent(Display, { user });
snnsnn
  • 10,486
  • 4
  • 39
  • 44
2

In Solid.js, the properties of the props object are getters that call signals. The JSX attributes are compiled into effects that update the DOM. Effects that call signals get subscribed to changes (unless the access to the signal is untracked). By destructuring the props object, you call the getters outside of an effect, so there is nothing to subscribe to changes of the underlying signal.

There are multiple solutions to circumvent this:

  1. avoid destructuring
  2. use @solid-primitives/destructure in your runtime code to retain reactivity in destructured props
  3. use babel-plugin-solid-undestructure in your build chain to replace destructuring with direct access in your runtime code.
lexLohr
  • 201
  • 1
  • 3
-1

Just some vanilla code to help aid the mental model (i.e. NOT the actual implementation) on why destructuring breaks reactive props using what was already mentioned in the other answers.

function makeProps(value: string) {
  const props = {
    get name() {
      return value;
    }
  };

  const set = (v: string) => void(value = v);
  const pair: [{ name: string }, (name: string) => void] = [props, set];
  return pair;
};

// 1. Create props with original value
const [props, set] = makeProps('John');

// 2. If we choose to destructure `props`
const { name } = props;

// 3. then something else (reactively) changes the props
set('Jarod');

// 4. When we use the value
console.log(props.name); // "Jarod" up-to-date 
console.log(name); // "John" stale 

The key piece is that props isn't a static { name: string } object (even if TypeScript describes it that way) but { get name() {...} }, an object with getters where the accessed values can change later.

With static objects destructuring doesn't matter because it's not going to change anyway but on an object with getters destructuring snapshots the values of its properties while the actual props values can change in time.

Furthermore in React component functions are render functions; an entirely new props object is supplied any time React re-renders the component.

In Solid component functions are setup functions which only run once in a component's lifetime to create the component's "reactive JSX". The component's props object (with getters) is created at the same time. The getters on the props allows the "reactive JSX" to obtain the updated values at a later point in time.

For a better understanding of how Solid's reactivity works have a look at Building a Reactive Library from Scratch.

Peer Reynders
  • 546
  • 3
  • 6
  • My answer was supposed to be a less complex «mental aid», NOT the actual implementation. The core of the confusion of novices is that they expect a props as a static { name: ... } when in fact they are dealing with { get name(){…} } which can change in time. But thanks for updating the technical details on your answer anyway. – Peer Reynders Jul 06 '23 at 14:12