SolidJS: 为什么解构 props 会导致响应性丢失?

huangapple go评论50阅读模式
英文:

SolidJS: Why does destructuring of props cause a loss of reactivity?

问题

Props(属性)是我们在执行组件函数时传递给它的对象,代表了与其JSX绑定的所有属性。Props对象是只读的,并且具有反应性属性,这些属性包装在Object getters中。这使它们无论调用者是使用信号(signals)、信号表达式(signal expressions)还是静态值,都具有一致的形式。您可以通过props.propName来访问它们。

因此,非常重要的一点是不要仅仅解构props对象,因为如果不在跟踪范围内进行解构,就会失去响应性。一般来说,在Solid的基元或JSX之外访问props对象上的属性可能会导致失去响应性。这不仅适用于解构,还适用于扩展和类似Object.assign的函数。

我在Solid JS教程中找到了这段内容:https://www.solidjs.com/tutorial/props_defaults。但我仍然不理解如何解构props会导致丧失响应性。

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

我不知道它们之间有什么区别。我认为我还不太理解SolidJS中响应性是如何工作的。

它们是否在组件函数和JSX方面有更多的功能?

英文:

> 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 &lt;div&gt;{name}&lt;/div&gt;
 return &lt;div&gt;{props.name}&lt;/div&gt;

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?

答案1

得分: 3

因为在解构时,响应式值被提取为普通的静态变量。然后这些值不会更新。

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

// 现在 name 是一个静态变量
const { name } = user();

顺便说一下,props 不是普通对象,而是传递属性的 getter 方法,因此它们使用隐式函数调用方式,这就是它们保持响应式的方式。

如果你检查编译后的代码:

<Display user={user()} />

你会看到它编译成了这样:

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

你可能会问编译器如何知道它是一个信号,它其实不知道。这对所有函数调用都适用。

如果你需要退出这种方式,请在信号前面加上 @once 注释:

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

现在值将变为静态:

_$createComponent(Display, { user });
英文:

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

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

// 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:

&lt;Display user={user()} /&gt;

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:

&lt;Display user={/*@once*/user()} /&gt;

Now value will be static:

_$createComponent(Display, { user });

答案2

得分: 2

在Solid.js中,props对象的属性是调用信号的getter。JSX属性被编译成会更新DOM的effects。调用信号的effects会订阅变化(除非信号的访问是未跟踪的)。通过解构props对象,您在effect之外调用这些getter,因此没有订阅底层信号变化的内容。

有多种解决方法来规避这个问题:

  1. 避免解构
  2. 在您的运行时代码中使用 @solid-primitives/destructure 以保留解构props的响应性
  3. 在您的构建链中使用 babel-plugin-solid-undestructure 以在运行时代码中用直接访问替换解构。
英文:

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.

答案3

得分: -1

以下是您要翻译的内容:

"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."

请注意,这是英文内容,您已要求不翻译代码部分,所以只提供英文内容的翻译。如果您有其他要求,请告诉我。

英文:

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) =&gt; void(value = v);
  const pair: [{ name: string }, (name: string) =&gt; void] = [props, set];
  return pair;
};

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

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

// 3. then something else (reactively) changes the props
set(&#39;Jarod&#39;);

// 4. When we use the value
console.log(props.name); // &quot;Jarod&quot; up-to-date &#128077;
console.log(name); // &quot;John&quot; stale &#128078;

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.

huangapple
  • 本文由 发表于 2023年5月17日 21:38:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76272753.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定