حلقهها - چگونه با استفاده از حاشیهنویسیهای تایپاسکریپت بر روی فرزندان solid-js تکرار کنیم


فرض کنید من کامپوننت زیر را دارم، چگونه میتوانم روی هر فرزند پیمایش کرده و آن را رندر کنم تا کارهایی مانند پیچیدن هر فرزند با کامپوننتهای دیگر انجام دهم؟

interface StuffProps {
  children: `?`
}

function Stuff({ children }: StuffProps) {
  // ?
}

من سعی کردهام children: JSX.Element را تنظیم کنم و سپس <For each={children}> ... </For> را انجام دهم، اما این به من یک خطای تایپ اسکریپت میدهد.

پاسخ شماره ۱

اگر قصد ندارید از کودکان در برخی افکتها استفاده کنید، میتوانید کودکان را مستقیماً با یک عنصر JSX بپیچید:

import { Component, JSXElement } from 'solid-js';
import { render } from 'solid-js/web';

const Stuff: Component<{ children: JSXElement }> = (props) => {
  return (
    <div><span>Wrapper:{props.children}</span></div>
  )
};

const App = () => {
  return (
    <div>
      <Stuff>
        <h1>Some Title</h1>
      </Stuff>

      <Stuff>
        <h1>Other Title</h1>
        <p>Some paragraph</p>
      </Stuff>
    </div>
  );
}

render(() => <App />, document.body);

در اینجا عمداً از تجزیهٔ props اجتناب میکنیم زیرا باعث میشود واکنشپذیری از بین برود، چون آنها را به یک متغیر محلی اختصاص میدهید. واکنشپذیری بین کامپوننتها از طریق فراخوانی توابع منتقل میشود. اما من برای وضوح، فعلاً این قانون را نادیده میگیرم.

اگر میخواهید از props.children با کامپوننتهای For استفاده کنید، خطا خواهید گرفت زیرا children تضمینشده نیست که یک آرایه باشد، چون عنصر JSX میتواند هرکدام از موارد number | boolean | Node | JSX.ArrayElement | JSX.FunctionElement | (string & {}) | null | undefined باشد.

پس، شما باید مطمئن شوید که children یک آرایه است:

import { render, } from 'solid-js/web';
import { Component, JSXElement } from 'solid-js';

const Stuff: Component<{ children: JSXElement }> = (props) => {
  const children = Array.isArray(props.children) ? props.children : [props.children];
  return (
    <ul>
      <li>{children.map(child => child)}</li>
    </ul>
  )
};

const App = () => {
  return (
    <div>
      <Stuff>
        <h1>Some Title</h1>
      </Stuff>

      <Stuff>
        <h1>Other Title</h1>
        <p>Some paragraph</p>
      </Stuff>
    </div>
  );
}

render(() => <App />, document.body);

حال میتوانید از آن با کامپوننت For استفاده کنید:

import { render, } from 'solid-js/web';
import { Component, For, JSXElement } from 'solid-js';

const Stuff: Component<{ children: JSXElement }> = (props) => {
  const children = Array.isArray(props.children) ? props.children : [props.children];
  return (
    <ul>
      <For each={children}>
        {item => <li>{item}</li>}
      </For>
    </ul>
  )
};

const App = () => {
  return (
    <div>
      <Stuff>
        <h1>Some Title</h1>
      </Stuff>

      <Stuff>
        <h1>Other Title</h1>
        <p>Some paragraph</p>
      </Stuff>
    </div>
  );
}

render(() => <App />, document.body);

Solid این راحتی را از طریق تابع children از کتابخانه اصلی فراهم میکند، اما اگر از آنها با کامپوننت For استفاده کنید، باز هم خطای نوع دریافت خواهید کرد. دلیل آن این است که نتیجه میتواند false | readonly unknown[] | null | undefined باشد، اما این مشکل با استفاده از روش .toArray به جای فراخوانی تابع قابل رفع است:

import { render, } from 'solid-js/web';
import { children, Component, For, JSXElement } from 'solid-js';

const Stuff: Component<{ children: JSXElement }> = (props) => {
  const resolved = children(() => props.children);

  return (
    <ul>
      <For each={resolved.toArray()}>
        {item => <li>{item}</li>}
      </For>
    </ul>
  )
};

const App = () => {
  return (
    <div>
      <Stuff>
        <h1>Some Title</h1>
      </Stuff>

      <Stuff>
        <h1>Other Title</h1>
        <p>Some paragraph</p>
      </Stuff>
    </div>
  );
}

render(() => <App />, document.body);

همانطور که گفتم، این یک راحتی برای دسترسی به فرزندان داخل افکتها است. استخراج آنها فقط برای قرار دادن در یک عنصر دیگر خیلی منطقی نیست، چون میتوانید مستقیماً این کار را انجام دهید، مانند اینکه عبارات، عناصر JSX معتبر هستند:

<div>{props.children}</div>

پاسخ شماره ۲

مطمئن نیستم که این درست باشد، اما ممکن است پاسخی پیدا کرده باشم با استفاده از تابع کمکی children ارائه شده توسط solid-js (مستندات API).

راهحل من چیزی شبیه به این به نظر میرسد:

import { JSX, children } from 'solid-js';

interface StuffProps {
  children: JSX.Element
}

function Stuff(props: StuffProps) {
  const cs = children(() => props.children).toArray()
  <For each={cs}> ... </For>
}