prismicio / prismic-react

React components and hooks to fetch and present Prismic content
https://prismic.io/docs/technologies/homepage-reactjs
Apache License 2.0
153 stars 40 forks source link

Provide a helper that conditionally renders if a field is filled #199

Open angeloashmore opened 5 months ago

angeloashmore commented 5 months ago

Is your feature request related to a problem? Please describe.

Webpages commonly need to conditionally render content only if a content field has a value. For example, you may only want to render an <h1> element if a Title field has a value.

This can currently be accomplished with @prismicio/client's isFilled set of helpers:

import { isFilled } from "@prismicio/client";
import { PrismicRichText } from "@prismicio/react";

export function MySlice({ slice }) {
  return (
    <section>
      {isFilled.richText(slice.primary.title) ? (
        <h1>
          <PrismicRichText field={slice.primary.title} />
        </h1>
      ) : (
        <div>Some fallback</div>
      )}
    </section>
  );
}

While the above code works, it is verbose and feels procedural.

Describe the solution you'd like

A dedicated React component that accomplishes the same as above in a declarative way would be an improvement.

import { IsFilled, PrismicRichText } from "@prismicio/react";

export function MySlice({ slice }) {
  return (
    <section>
      <IsFilled.RichText
        field={slice.primary.title}
        fallback={<div>Some fallback</div>}
      >
        <h1>
          <PrismicRichText field={slice.primary.title} />
        </h1>
      </IsFilled.RichText>
    </section>
  );
}

Although the above code is not more concise than the isFilled.richText() version (it is actually more characters), it is easier to read and edit. The React syntax fits in with the surrounding code better than the ternarry and isFilled helper.

This solution can be ported to other integration libraries, like @prismicio/vue and @prismicio/svelte.

Type Narrowing

isFilled narrows the type of the provided field. After narrowing the field, you can be sure that the field is filled, and you will get autocompletion that suggests that.

When using JSX, you will not get that effect because TypeScript doesn't support type predicates in JSX (see Using type predicates).

To get around that limitation, we may need to introduce a dedicated render prop (render={(field) => {...}}) or render prop via children (i.e. (field) => {...}) rather than supporting children directly.

import { IsFilled, PrismicRichText } from "@prismicio/react";

export function MySlice({ slice }) {
  return (
    <section>
      <IsFilled.RichText
        field={slice.primary.title}
        fallback={<div>Some fallback</div>}
      >
        {(field) => (
          <h1>
            <PrismicRichText field={field} />
          </h1>
        )}
      </IsFilled.RichText>
    </section>
  );
}

Or...

import { IsFilled, PrismicRichText } from "@prismicio/react";

export function MySlice({ slice }) {
  return (
    <section>
      <IsFilled.RichText
        field={slice.primary.title}
        render={(field) => (
          <h1>
            <PrismicRichText field={field} />
          </h1>
        )}
        fallback={<div>Some fallback</div>}
      />
    </section>
  );
}

Either case may not work in Server Components since it passes a function.

Describe alternatives you've considered

  1. isFilled helpers, as described above.
  2. Pre-process query results to bake metadata into values. For example, a key text field could have a hidden _hasValue computed property that tells us if it is filled. Pre-processing content requires access to the type JSON definitions, which is not available on the client.
  3. Build-time generated that creates isFilled helpers specific for your content. It's not clear exactly what this is yet.

Additional context

N/A