如何将超过 1 个 ref 传递给 SolidJS 中的子组件?

How to pass more than 1 ref to a child component in SolidJS?

父组件:

function ParentComponent() {
 return (
    <section>
      <ChildComponent ref={sectionRef} ref1={headerRef} />
    </section>
  );
} 

子组件:

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>
  );
}

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

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

<ChildComponent ref={{sectionRef, headerRef}} />

和:

<ChildComponent ref={[sectionRef, headerRef]} />

和:

<ChildComponent section={sectionRef} header={headerRef} />

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

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

在组件中设置 Refs 时

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

  1. 传递常规变量
function Component() {
  let buttonEl;

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

  return (
    <button ref={buttonEl}>Click</button>
  );
}
  1. 通过信号setter
function Component() {
  const [buttonEl, setButtonEl] = createSignal(null);

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

  return <button ref={setButtonEl}>Click</button>;
}
  1. 通过回调
function Component() {
 let buttonEl;

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

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

 return <button ref={refCallback}>Click</button>;
}

在子组件中设置 Refs 时

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

  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>;
}
  1. 将信号 setter 传递给 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>;
}
  1. 将与 buttonEl 在相同范围内声明的回调传递给 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>;
}

要修复使用常规变量 let buttonEl; 的 #1 解决方案,请使用正确的组件属性 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>;
}

那么为什么这样做有效呢?好吧,因为在编译输出中,使用 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>;
}

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

解决方案

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

在这个解决方案示例中,sectionRefheaderRef 都是未分配的变量。 sectionRef 被传递给 ref 道具,在幕后,它被包裹在回调中。回调函数 refCallback 被传递给 ref1 属性,它设置 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>
  );
}

如何

再次重申。 Solid 使其工作的方式是在编译输出中,如果一个组件 属性 被命名为 ref,它被一个对象方法替换(在这种情况下具有与回调函数相同的策略)它位于创建“ref”变量(例如sectionRef)的同一位置,这样就可以将“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
    }));
    
    // ...

  })();
}