Skip to content

Commit cc8de36

Browse files
authored
Fix <With /> bug where children are not unmounted when given data is null | undefined. (#131)
1 parent 8174bc4 commit cc8de36

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

packages/@react-facet/core/src/components/With.spec.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React from 'react'
2-
import { render } from '@react-facet/dom-fiber-testing-library'
2+
import { act, render } from '@react-facet/dom-fiber-testing-library'
33
import { With } from '.'
44
import { createFacet } from '../facet'
55
import { Facet, NO_VALUE } from '../types'
6-
import { useFacetMap } from '../hooks'
6+
import { useFacetEffect, useFacetMap } from '../hooks'
77

88
it('renders when not null, passing down the information', () => {
99
const userFacet = createFacet({ initialValue: { user: 'Zelda' } })
@@ -65,3 +65,34 @@ it('does not render facet has no value', () => {
6565

6666
expect(rendered).not.toHaveBeenCalled()
6767
})
68+
69+
it('correctly handles unmounting', () => {
70+
const mockFacet = createFacet<string | null>({ initialValue: 'abc' })
71+
72+
const Content = ({ data }: { data: Facet<string> }) => {
73+
useFacetEffect(
74+
(data) => {
75+
if (data === null || data === undefined) {
76+
throw new Error('data should not be null')
77+
}
78+
},
79+
[],
80+
[data],
81+
)
82+
83+
return <>mounted</>
84+
}
85+
86+
const Example = () => {
87+
return <With data={mockFacet}>{(mock) => <Content data={mock} />}</With>
88+
}
89+
const scenario = <Example />
90+
91+
const result = render(scenario)
92+
93+
expect(result.container).toHaveTextContent('mounted')
94+
95+
act(() => mockFacet.set(null))
96+
97+
expect(result.container).not.toHaveTextContent('mounted')
98+
})
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { ReactElement } from 'react'
2+
import { useFacetMemo } from '../hooks/useFacetMemo'
23
import { useFacetUnwrap } from '../hooks/useFacetUnwrap'
34
import { useFacetMap } from '../hooks/useFacetMap'
4-
import { Facet, NoValue } from '../types'
5+
import { Facet, NO_VALUE } from '../types'
56

67
type WithProps<T> = {
78
data: Facet<T | null | undefined>
89
children: (data: Facet<T>) => ReactElement | null
910
}
1011

11-
const hasData = <T,>(_: Facet<T | null | undefined>, shouldRender: boolean | NoValue): _ is Facet<T> => {
12-
return shouldRender === true
13-
}
14-
12+
/**
13+
* Conditionally renders a child if a given facet value is not null or undefined
14+
*
15+
* @param data facet value which can be null or undefined
16+
* @param children render prop which receives the transformed facet
17+
*/
1518
export const With = <T,>({ data, children }: WithProps<T>) => {
1619
const shouldRenderFacet = useFacetMap((data) => data !== null && data !== undefined, [], [data])
1720
const shouldRender = useFacetUnwrap(shouldRenderFacet)
18-
return hasData(data, shouldRender) ? children(data) : null
21+
const nonNullData = useFacetMemo((data) => (data !== null && data !== undefined ? data : NO_VALUE), [], [data])
22+
return shouldRender === true ? children(nonNullData) : null
1923
}

0 commit comments

Comments
 (0)