Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit 70063f2

Browse files
authored
fix(Popup): opened popup causes infinite rendering loop (#705)
* introduce fix * add async popup position update example * update changelog
1 parent dcead69 commit 70063f2

File tree

4 files changed

+46
-5
lines changed

4 files changed

+46
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
4242
- Fix shorthand prop type @kuzhelov ([#697](https://github.com/stardust-ui/react/pull/697))
4343
- Export `ShorthandRenderer` type @miroslavstastny ([#698](https://github.com/stardust-ui/react/pull/698))
4444
- Temporary revert `pxToRem` changes introduced by [#371](https://github.com/stardust-ui/react/pull/371) @kuzhelov ([#700](https://github.com/stardust-ui/react/pull/700))
45+
- Prevent infinite rendering loop start on `Popup` open @kuzhelov ([#705](https://github.com/stardust-ui/react/pull/705))
4546

4647
### Documentation
4748
- Add ability to edit examples' code in JavaScript and TypeScript @layershifter ([#650](https://github.com/stardust-ui/react/pull/650))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as React from 'react'
2+
import { Button, Popup, Segment } from '@stardust-ui/react'
3+
4+
class AsyncDataLoader extends React.Component<any, any> {
5+
state = {
6+
data: 'loading..',
7+
}
8+
9+
componentDidMount() {
10+
setTimeout(() => {
11+
this.setState({
12+
data: <Segment styles={{ minHeight: '300px' }}>Hello from loaded data!</Segment>,
13+
})
14+
this.props.onLoaded()
15+
}, 1000)
16+
}
17+
18+
render() {
19+
return this.state.data
20+
}
21+
}
22+
23+
const PopupExampleAsync = () => (
24+
<Popup
25+
trigger={<Button icon="expand" content="Click me!" />}
26+
renderContent={updatePosition => ({ content: <AsyncDataLoader onLoaded={updatePosition} /> })}
27+
/>
28+
)
29+
30+
export default PopupExampleAsync

docs/src/examples/components/Popup/Usage/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
55

66
const Usage = () => (
77
<ExampleSection title="Usage">
8+
<ComponentExample
9+
title="Async popup position update"
10+
description="A popup can be forced to update its position - this comes in handy in async data loading scenarios."
11+
examplePath="components/Popup/Usage/PopupExampleAsync"
12+
/>
813
<ComponentExample
914
title="Triggering popup on different actions"
1015
description="A popup can be triggered on click, hover or focus."

src/components/Popup/Popup.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ export interface PopupProps
9292
*/
9393
position?: Position
9494

95+
/**
96+
* Function to render popup content.
97+
* @param {Function} updatePosition - function to request popup position update.
98+
*/
99+
renderContent?: (updatePosition: Function) => ShorthandValue
100+
95101
/**
96102
* DOM element that should be used as popup's target - instead of 'trigger' element that is used by default.
97103
*/
@@ -140,6 +146,7 @@ export default class Popup extends AutoControlledComponent<ReactProps<PopupProps
140146
open: PropTypes.bool,
141147
onOpenChange: PropTypes.func,
142148
position: PropTypes.oneOf(POSITIONS),
149+
renderContent: PropTypes.func,
143150
target: PropTypes.any,
144151
trigger: PropTypes.any,
145152
}
@@ -411,9 +418,11 @@ export default class Popup extends AutoControlledComponent<ReactProps<PopupProps
411418
popupPositionClasses: string,
412419
rtl: boolean,
413420
accessibility: AccessibilityBehavior,
421+
// https://popper.js.org/popper-documentation.html#Popper.scheduleUpdate
414422
{ ref, scheduleUpdate, style: popupPlacementStyles }: PopperChildrenProps,
415423
) => {
416-
const { content } = this.props
424+
const { content: propsContent, renderContent } = this.props
425+
const content = renderContent ? renderContent(scheduleUpdate) : propsContent
417426

418427
const popupWrapperAttributes = {
419428
...(rtl && { dir: 'rtl' }),
@@ -443,10 +452,6 @@ export default class Popup extends AutoControlledComponent<ReactProps<PopupProps
443452
overrideProps: this.getContentProps,
444453
})
445454

446-
// Schedules a position update after each render.
447-
// https://popper.js.org/popper-documentation.html#Popper.scheduleUpdate
448-
scheduleUpdate()
449-
450455
return (
451456
<Ref
452457
innerRef={domElement => {

0 commit comments

Comments
 (0)