如何在 SolidJS 中将超过 1 个引用传递给子组件?

Joe*_*e C 8 solid-js

父组件:

function ParentComponent() {
 return (
    <section>
      <ChildComponent ref={sectionRef} ref1={headerRef} />
    </section>
  );
} 
Run Code Online (Sandbox Code Playgroud)

子组件:

function ChildComponent(props) {
return (
    <section ref={props.ref}>
      <article>
        <h2 ref={props.ref1}>Lorem Ipsum</h2>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia,
          molestiae quas vel sint commodi repudiandae consequuntur voluptatum laborum
          numquam blanditiis harum quisquam eius sed odit fugiat iusto fuga praesentium optio, eaque rerum!</p>
      </article>
    </section>
  );
}
Run Code Online (Sandbox Code Playgroud)

我的目标是能够从父组件中定位子组件中的不同 DOM 元素,以便我可以根据父组件的滚动事件为它们设置动画。

我尝试将引用作为不同的数据结构传递:

<ChildComponent ref={{sectionRef, headerRef}} />
Run Code Online (Sandbox Code Playgroud)

和:

<ChildComponent ref={[sectionRef, headerRef]} />
Run Code Online (Sandbox Code Playgroud)

和:

<ChildComponent section={sectionRef} header={headerRef} />
Run Code Online (Sandbox Code Playgroud)

但不断出现第二个引用未定义的错误。只有当我为每个子组件传递一个引用时,我才能让它工作。有任何想法吗?

我查看的参考材料的链接: https: //www.solidjs.com/tutorial/bindings_forward_refs https://www.solidjs.com/docs/latest/api#ref

Cal*_*lor 14

当在 Components 中设置 Refs 时

有 3 种方法可以在组件内引用 DOM 元素。

  1. 传递正则变量
function Component() {
  let buttonEl;

  onMount(() => {
    console.log(buttonEl) // logs button element
  }) 

  return (
    <button ref={buttonEl}>Click</button>
  );
}
Run Code Online (Sandbox Code Playgroud)
  1. 通过信号设置器
function Component() {
  const [buttonEl, setButtonEl] = createSignal(null);

  onMount(() => {
    console.log(buttonEl()) // logs button element
  }) 

  return <button ref={setButtonEl}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)
  1. 传递回调
function Component() {
 let buttonEl;

 const refCallback = (el) => {
   buttonEl = el;
 };

 onMount(() => {
   console.log(buttonEl); // logs button element
 });

 return <button ref={refCallback}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)

当在子组件中设置 Refs 时

然而,当引用子组件中设置的 DOM 元素时,情况就不同了。为了演示,我们不会ref对子组件使用 prop,而是使用PASSREFprop.

  1. 将常规变量传递给PASSREF. 该变量不会更新,并且是undefined
function Component() {
  let buttonEl;

  onMount(() => {
    console.log(buttonEl); // logs `undefined`
  });

  return <Child PASSREF={buttonEl} />;
}

function Child(props) {
  return <button ref={props.PASSREF}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)
  1. 将信号设置器传递给PASSREF,起作用。
function Component() {
  const [buttonEl, setButtonEl] = createSignal(null)

  onMount(() => {
    console.log(buttonEl()); // logs button element
  });

  return <Child PASSREF={setButtonEl} />;
}

function Child(props) {
  return <button ref={props.PASSREF}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)
  1. 传递回调,该回调在与buttonEl, to相同的范围内声明PASSREF,有效。
function Component() {
 let buttonEl;

 const refCallback = (el) => {
   buttonEl = el;
 };

 onMount(() => {
   console.log(buttonEl); // logs button element
 });

 return <Child PASSREF={refCallback} />;
}

function Child(props) {
 return <button ref={props.PASSREF}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)

要修复使用常规变量的#1 解决方案let buttonEl;,您可以使用正确的组件属性ref来将元素设置为变量。

function Component() {
  let buttonEl;

  onMount(() => {
    console.log(buttonEl); // logs button element
  });

  return <Child ref={buttonEl} />;
}

function Child(props) {
  return <button ref={props.ref}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)

那么为什么这会起作用呢?因为在编译的输出中,使用 ref 的 Child prop 参数实际上被内联回调替换,这样它就位于buttonEl声明并可以更新的相同范围内。

// This is NOT how the Compiled Output actually looks, 
// but ref argument is replaced by an inline callback
function Component() {
  let buttonEl;

  onMount(() => {
    console.log(buttonEl); // logs button element
  });

  return <Child ref={(el) => buttonEl = el} />;
}

function Child(props) {
  return <button ref={props.ref}>Click</button>;
}
Run Code Online (Sandbox Code Playgroud)

是不是看起来很眼熟?这种结构几乎与 #3 解决方案完全相同,您可以在其中传递回调函数来更新buttonEl.

解决方案

老实说,这取决于您的用例,要么使用信号设置器 fromcreateSignal来传递引用,要么使用父级中声明的回调函数来设置普通变量。

在此解决方案示例中, 和sectionRef都是headerRef未分配的变量。sectionRef被传递给refprop,在幕后,它被包装在回调中。回调函数refCallback被传递到ref1prop,并在其中设置headerRef传递的元素值。

function ParentComponent() {
  let sectionRef;
  let headerRef;

  const refCallback = (el) => {
    headerRef = el
  }

  onMount(() => {
    console.log(sectionRef); // logs section el
    console.log(headerRef); // logs header el
  });

  return (
    <section>
      <Overview ref={sectionRef} ref1={refCallback} />
    </section>
  );
}

function Overview(props) {
  return (
    <section ref={props.ref}>
      <article>
        <h2 ref={props.ref1}>Lorem Ipsum</h2>
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime
          mollitia, molestiae quas vel sint commodi repudiandae consequuntur
          voluptatum laborum numquam blanditiis harum quisquam eius sed odit
          fugiat iusto fuga praesentium optio, eaque rerum!
        </p>
      </article>
    </section>
  );
}
Run Code Online (Sandbox Code Playgroud)

如何

再次重申一下。Solid 使其工作的方式是,在编译的输出中,如果组件属性被命名为ref,它会被替换为对象方法(在此上下文中具有与回调函数相同的策略),该方法位于“sectionRef创建“ ref”变量(例如),这样就可以将“ref”变量分配给它。

如果您好奇,这里是解决方案的实际编译输出,您可以在其中看到ref实际的样子。

// Compiled Output

function ParentComponent() {
  let sectionRef;
  let headerRef;

  const refCallback = el => {
    headerRef = el;
  };

  // ...

  return (() => {
    const _el$ = _tmpl$.cloneNode(true);

    insert(_el$, createComponent(Overview, {
      ref(r$) {
        const _ref$ = sectionRef;
        typeof _ref$ === "function" ? _ref$(r$) : sectionRef = r$;
      },
      ref1: refCallback
    }));
    
    // ...

  })();
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的详尽解释。我学到了一些非常有用的东西。 (2认同)