Skip to content

Commit af534f1

Browse files
authored
Fix slots infinite rendering when no context prop is provided (#2219)
* Add basic tests for `createSlots` * Fix infinite slot rendering when context prop not provided * Create changelog * Update to use existing tests
1 parent 47725a9 commit af534f1

File tree

3 files changed

+15
-7
lines changed

3 files changed

+15
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
Fix slots infinite rendering when no `context` prop is provided

src/__tests__/utils/createSlots.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import createSlots from '../../utils/create-slots'
55

66
// setup a component with slots
77
const {Slots, Slot} = createSlots(['One', 'Two', 'Three'])
8-
type ContextTypes = {salutation?: string}
8+
type Props = {context?: {salutation: string}}
99

10-
const ComponentWithSlots: React.FC<React.PropsWithChildren<ContextTypes>> = ({salutation, children}) => {
10+
const ComponentWithSlots: React.FC<React.PropsWithChildren<Props>> = ({context, children}) => {
1111
return (
12-
<Slots context={{salutation}}>
12+
<Slots context={context}>
1313
{slots => (
1414
<div>
1515
{slots.One}
@@ -25,9 +25,9 @@ const SlotItem1: React.FC<React.PropsWithChildren<unknown>> = ({children}) => <S
2525
const SlotItem2: React.FC<React.PropsWithChildren<unknown>> = ({children}) => <Slot name="Two">{children}</Slot>
2626
const SlotItem3: React.FC<React.PropsWithChildren<unknown>> = ({children}) => (
2727
<Slot name="Three">
28-
{(context: ContextTypes) => (
28+
{(context: Props['context']) => (
2929
<>
30-
{context.salutation} {children}
30+
{context?.salutation} {children}
3131
</>
3232
)}
3333
</Slot>
@@ -64,7 +64,7 @@ describe('ComponentWithSlots', () => {
6464

6565
it('renders with context passed to children', async () => {
6666
const component = render(
67-
<ComponentWithSlots salutation="hi">
67+
<ComponentWithSlots context={{salutation: 'hi'}}>
6868
<SlotItem3>third</SlotItem3>
6969
free form
7070
</ComponentWithSlots>

src/utils/create-slots.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const createSlots = <SlotNames extends string>(slotNames: SlotNames[]) => {
2323
context: {}
2424
})
2525

26+
// maintain a static reference to avoid infinite render loop
27+
const defaultContext = Object.freeze({})
28+
2629
/** Slots uses a Double render strategy inspired by [reach-ui/descendants](https://github.com/reach/reach-ui/tree/develop/packages/descendants)
2730
* Slot registers themself with the Slots parent.
2831
* When all the children have mounted = registered themselves in slot,
@@ -33,7 +36,7 @@ const createSlots = <SlotNames extends string>(slotNames: SlotNames[]) => {
3336
context?: ContextProps['context']
3437
children: (slots: Slots) => React.ReactNode
3538
}>
36-
> = ({context = {}, children}) => {
39+
> = ({context = defaultContext, children}) => {
3740
// initialise slots
3841
const slotsDefinition: Slots = {}
3942
slotNames.map(name => (slotsDefinition[name] = null))

0 commit comments

Comments
 (0)