prismicio / prismic-react

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

[RFC] Passing additional Properties in SliceZone with SliceProps #113

Closed phillysnow closed 2 years ago

phillysnow commented 2 years ago

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

Some users would like to pass variables and functions down to Slices through the Slice Zone.

I need to pass several functions and variables to the slices, predominately for event listeners that will affect other areas on the website which are not slices.

So far, the only way I seem to be able to pass these properties to the slices is by using the 'context' parameter.

Describe the solution you'd like

Something like the sliceProps property in next-slicezone: https://github.com/prismicio/slice-machine/tree/master/packages/next-slicezone#slicezone

Additional context

Forum Thread: https://community.prismic.io/t/passing-additional-properties-in-slicezone-with-sliceprops/6927/11

lihbr commented 2 years ago
Explaining the context prop and why upgrading to the new <SliceZone /> components Hey Phil, Moving forward `` components from `next-slicezone` (and `vue-slicezone`) will be deprecated in favor of `@prismicio/react` (and `@prismicio/vue`) new `` components. Those new `` components have been released as part of their related kits (`@prismicio/react`, `@prismicio/vue`, `@nuxtjs/prismic`) and work similarly to the existing ones (with some minor tweaks needed) so I'd recommend starting to move code over to these new ones. To do so, technical references are here to help: - React: https://prismic.io/docs/technical-reference/prismicio-react#slicezone - Vue: https://prismic.io/docs/technical-reference/prismicio-vue#slicezone One of the great thing with those new `` components is that they both support a `context` prop, allowing to pass any type of data to the rendered slices ☺️ ![image](https://user-images.githubusercontent.com/25330882/149924744-12cf7c5a-7da3-4f25-a875-51dd2cd48260.png)
lihbr commented 2 years ago

I have mixed feelings about this idea as it feels wrong having to write some kind of "prop resolver". Assuming you have a <FooBar /> slice that needs events, can't the context prop carryover data needed for the <FooBar /> slice to emit necessary events?

Curious on @angeloashmore's opinion

angeloashmore commented 2 years ago

There are two recommended ways to pass data to Slice components. Using one of these two methods is preferred, in my opinion, over adding an additional sliceProps prop containing potentially complex logic.

I am curious to hear if there is a use case where one of the two options cannot accomplish what you need. Maybe there is a need for another prop that I missed. :)

1. Treat React components as functions

Pass Slice-specific data to the components in the components object. Because a React component is a function (we could also call it a "resolver"), something like the sliceProps prop from next-slicezone is built-in. For example:

<SliceZone
  slices={doc.data.body}
  components={{
    text: (props) => (
      <TextSlice myCustomProp={myCustomData} {...props} />
    ),
    images: ImagesSlice,
  }}
/>

In this example, the text Slice will render a <TextSlice> component with a myCustomProp value. Because the props argument has all information about the slice, including the whole slice object, it can also be used to compute these custom props. Just remember to pass any used properties as props again.

<SliceZone
  slices={doc.data.body}
  components={{
    text: ({ slice, ...props }) => (
      <TextSlice theme={slice.primary.theme} slice={slice} {...props} />
    ),
    images: ImagesSlice,
  }}
/>

Here, the theme props is given a value derived from the slice object. slice is passed explicitly to the component so it can be used elsewhere.

2. Pass arbitrary data to the context prop

Any data passed to the context prop will be passed to all Slices. How the data is used and how it is shaped is totally up to you depending on how you need to use it. One example is providing an object keyed by the Slice type that needs the data. This organizes data in a scalable way.

<SliceZone
  slices={doc.data.body}
  components={{
    text: ({ slice, ...props }) => (
      <TextSlice theme={slice.primary.theme} slice={slice} {...props} />
    ),
    images: ImagesSlice,
  }}
  context={{
    text: {
      myCustomData: "hello!",
    }
  }}
/>

If this custom data is only used in one or a few Slices, consider using the first method ("Treat React components as functions") and pass the data directly to the component.

If you need this custom data in all Slices, then use the context prop.

OTaylorUK commented 2 years ago

@angeloashmore Thanks so much for your help, this is all really helpful and I'll definitely be using a mix of both methods in future projects.

Currently, I'm using the context prop to pass properties to all the Slices, which works great but it's really helpful to see how I'd pass it to a specific Slice instead of all. Thanks again!

angeloashmore commented 2 years ago

Great! Happy to hear it was helpful.

I'm going to close this feature request as I believe it has already been addressed. If you have any additional feedback or thoughts, please feel free to comment here and we can continue the discussion.

Thanks!