Skip to content

Commit 1a145fd

Browse files
authored
Remove forceRerender from Tab component (#1846)
* remove `forceRerender` code This was necessary to ensure the `Panel` and the `Tab` were properly connected with eachother because it could happen that the `Tab` renders but the corresponding `Panel` is not the active one which means that it didn't have a DOM node and no `id` attached. Whenever new `Tab` became active, it rerendered but the `Panel` wasn't available yet, after that the `Panel` rendered and an `id` was available but the actual `Tab` was already rendered so there was no link between them. We then forced a re-render because now the `Panel` does have a DOM node ref attached and the `aria-labelledby` could be filled in. However, in #1837 we fixed an issue where the order of `Tab` elements with their corresponding `Panel` elements weren't always correct. To fix this we ensured that the `Panel` always rendered a `<Hidden />` component which means that a DOM node is always available. This now means that we can get rid of the `forceRerender`. * update changelog
1 parent 46a7ab6 commit 1a145fd

File tree

3 files changed

+53
-18
lines changed

3 files changed

+53
-18
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Add `<fieldset disabled>` check to radio group options in React ([#1835](https://github.com/tailwindlabs/headlessui/pull/1835))
1414
- Ensure `Tab` order stays consistent, and the currently active `Tab` stays active ([#1837](https://github.com/tailwindlabs/headlessui/pull/1837))
1515
- Ensure `Combobox.Label` is properly linked when rendered after `Combobox.Button` and `Combobox.Input` components ([#1838](https://github.com/tailwindlabs/headlessui/pull/1838))
16+
- Remove `forceRerender` from `Tab` component ([#1846](https://github.com/tailwindlabs/headlessui/pull/1846))
1617

1718
## [1.7.0] - 2022-09-06
1819

packages/@headlessui-react/src/components/tabs/tabs.test.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,56 @@ describe('Rendering', () => {
168168
)
169169

170170
describe('`renderProps`', () => {
171+
it(
172+
'should be possible to render using as={Fragment}',
173+
suppressConsoleLogs(async () => {
174+
render(
175+
<Tab.Group>
176+
<Tab.List>
177+
<Tab as={React.Fragment}>
178+
<button>Tab 1</button>
179+
</Tab>
180+
<Tab>Tab 2</Tab>
181+
<Tab>Tab 3</Tab>
182+
</Tab.List>
183+
184+
<Tab.Panels>
185+
<Tab.Panel>Content 1</Tab.Panel>
186+
<Tab.Panel>Content 2</Tab.Panel>
187+
<Tab.Panel>Content 3</Tab.Panel>
188+
</Tab.Panels>
189+
</Tab.Group>
190+
)
191+
192+
assertTabs({ active: 0, tabContents: 'Tab 1', panelContents: 'Content 1' })
193+
})
194+
)
195+
196+
it(
197+
'should be possible to render using multiple as={Fragment}',
198+
suppressConsoleLogs(async () => {
199+
render(
200+
<Tab.Group>
201+
<Tab.List>
202+
<Tab as={React.Fragment}>
203+
<button>Tab 1</button>
204+
</Tab>
205+
<Tab as={React.Fragment}>
206+
<button>Tab 2</button>
207+
</Tab>
208+
</Tab.List>
209+
210+
<Tab.Panels>
211+
<Tab.Panel>Content 1</Tab.Panel>
212+
<Tab.Panel>Content 2</Tab.Panel>
213+
</Tab.Panels>
214+
</Tab.Group>
215+
)
216+
217+
assertTabs({ active: 0, tabContents: 'Tab 1', panelContents: 'Content 1' })
218+
})
219+
)
220+
171221
it(
172222
'should expose the `selectedIndex` on the `Tab.Group` component',
173223
suppressConsoleLogs(async () => {

packages/@headlessui-react/src/components/tabs/tabs.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ enum ActionTypes {
4444

4545
RegisterPanel,
4646
UnregisterPanel,
47-
48-
ForceRerender,
4947
}
5048

5149
type Actions =
@@ -54,7 +52,6 @@ type Actions =
5452
| { type: ActionTypes.UnregisterTab; tab: MutableRefObject<HTMLElement | null> }
5553
| { type: ActionTypes.RegisterPanel; panel: MutableRefObject<HTMLElement | null> }
5654
| { type: ActionTypes.UnregisterPanel; panel: MutableRefObject<HTMLElement | null> }
57-
| { type: ActionTypes.ForceRerender }
5855

5956
let reducers: {
6057
[P in ActionTypes]: (
@@ -110,9 +107,6 @@ let reducers: {
110107
[ActionTypes.UnregisterPanel](state, action) {
111108
return { ...state, panels: state.panels.filter((panel) => panel !== action.panel) }
112109
},
113-
[ActionTypes.ForceRerender](state) {
114-
return { ...state }
115-
},
116110
}
117111

118112
let TabsSSRContext = createContext<MutableRefObject<{ tabs: string[]; panels: string[] }> | null>(
@@ -154,7 +148,6 @@ let TabsActionsContext = createContext<{
154148
registerTab(tab: MutableRefObject<HTMLElement | null>): () => void
155149
registerPanel(panel: MutableRefObject<HTMLElement | null>): () => void
156150
change(index: number): void
157-
forceRerender(): void
158151
} | null>(null)
159152
TabsActionsContext.displayName = 'TabsActionsContext'
160153

@@ -229,9 +222,6 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
229222
dispatch({ type: ActionTypes.RegisterPanel, panel })
230223
return () => dispatch({ type: ActionTypes.UnregisterPanel, panel })
231224
},
232-
forceRerender() {
233-
dispatch({ type: ActionTypes.ForceRerender })
234-
},
235225
change(index: number) {
236226
if (realSelectedIndex.current !== index) {
237227
onChangeRef.current(index)
@@ -335,10 +325,7 @@ let TabRoot = forwardRefWithAs(function Tab<TTag extends ElementType = typeof DE
335325
let SSRContext = useSSRTabsCounter('Tab')
336326

337327
let internalTabRef = useRef<HTMLElement | null>(null)
338-
let tabRef = useSyncRefs(internalTabRef, ref, (element) => {
339-
if (!element) return
340-
requestAnimationFrame(() => actions.forceRerender())
341-
})
328+
let tabRef = useSyncRefs(internalTabRef, ref)
342329

343330
useIsoMorphicEffect(() => actions.registerTab(internalTabRef), [actions, internalTabRef])
344331

@@ -492,10 +479,7 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
492479

493480
let id = `headlessui-tabs-panel-${useId()}`
494481
let internalPanelRef = useRef<HTMLElement | null>(null)
495-
let panelRef = useSyncRefs(internalPanelRef, ref, (element) => {
496-
if (!element) return
497-
requestAnimationFrame(() => actions.forceRerender())
498-
})
482+
let panelRef = useSyncRefs(internalPanelRef, ref)
499483

500484
useIsoMorphicEffect(() => actions.registerPanel(internalPanelRef), [actions, internalPanelRef])
501485

0 commit comments

Comments
 (0)