Skip to content

Commit

Permalink
chore: Should not re-render when dialog is invisible (#212)
Browse files Browse the repository at this point in the history
* fix: Dialog should not update children when !visible

* chore: test to tsx

* test: fix test case
  • Loading branch information
zombieJ authored Nov 16, 2020
1 parent 99ebd4f commit cba467a
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 14 deletions.
7 changes: 7 additions & 0 deletions examples/bootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import '../assets/bootstrap.less';
import * as React from 'react';
import Dialog from '../src/DialogWrap';

// Check for memo update should work
const InnerRender = () => {
console.log('Updated...', Date.now());
return null;
};

const MyControl = () => {
const [visible, setVisible] = React.useState(false);
const [destroyOnClose, setDestroyOnClose] = React.useState(false);
Expand Down Expand Up @@ -37,6 +43,7 @@ const MyControl = () => {
</button>,
]}
>
<InnerRender />
<h4>Text in a modal</h4>
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula.</p>
<hr />
Expand Down
11 changes: 11 additions & 0 deletions src/Dialog/Content/MemoChildren.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from 'react';

export interface MemoChildrenProps {
visible: boolean;
children: React.ReactNode;
}

export default React.memo(
({ children }: MemoChildrenProps) => children as React.ReactElement,

This comment has been minimized.

Copy link
@chenshuai2144

chenshuai2144 Dec 7, 2020

Contributor

forceRender 的情况下应该触发更新的,但是这里没有处理

(_, { visible }) => !visible,
);
9 changes: 6 additions & 3 deletions src/Dialog/Content.tsx → src/Dialog/Content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import * as React from 'react';
import { useRef } from 'react';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import { IDialogChildProps } from '.';
import { offset } from '../util';
import { IDialogChildProps } from '..';
import { offset } from '../../util';
import MemoChildren from './MemoChildren';

const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };

Expand Down Expand Up @@ -147,7 +148,9 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
onClick={onClick}
>
<div tabIndex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
{modalRender ? modalRender(content) : content}
<MemoChildren visible={visible}>
{modalRender ? modalRender(content) : content}
</MemoChildren>
<div tabIndex={0} ref={sentinelEndRef} style={sentinelStyle} aria-hidden="true" />
</div>
)}
Expand Down
46 changes: 35 additions & 11 deletions tests/index.spec.js → tests/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ describe('dialog', () => {
jest.runAllTimers();
wrapper.update();

document.getElementsByClassName('.test-input').value = 'test';
expect(document.getElementsByClassName('.test-input').value).toBe('test');
((document.getElementsByClassName('.test-input') as unknown) as HTMLInputElement).value =
'test';
expect(
((document.getElementsByClassName('.test-input') as unknown) as HTMLInputElement).value,
).toBe('test');

// Hide
wrapper.setProps({ visible: false });
Expand All @@ -103,7 +106,9 @@ describe('dialog', () => {
jest.runAllTimers();
wrapper.update();

expect(document.getElementsByClassName('.test-input').value).toBeUndefined();
expect(
((document.getElementsByClassName('.test-input') as unknown) as HTMLInputElement).value,
).toBeUndefined();
wrapper.unmount();
});
});
Expand Down Expand Up @@ -201,7 +206,9 @@ describe('dialog', () => {
describe('Tab should keep focus in dialog', () => {
it('basic tabbing', () => {
const wrapper = mount(<Dialog visible />, { attachTo: document.body });
const sentinelEnd = document.querySelectorAll('.rc-dialog-content + div')[0];
const sentinelEnd = (document.querySelectorAll(
'.rc-dialog-content + div',
)[0] as unknown) as HTMLDivElement;
sentinelEnd.focus();

wrapper.find('.rc-dialog-wrap').simulate('keyDown', {
Expand Down Expand Up @@ -237,16 +244,13 @@ describe('dialog', () => {
// Trigger position align
act(() => {
wrapper
.find('Content CSSMotion')
.find<any>('Content CSSMotion' as any)
.props()
.onAppearPrepare();
});

expect(
wrapper
.find('.rc-dialog')
.at(0)
.getDOMNode().style['transform-origin'],
(wrapper.find('.rc-dialog').at(0).getDOMNode() as HTMLDivElement).style['transform-origin'],
).toBeTruthy();
});

Expand Down Expand Up @@ -318,7 +322,7 @@ describe('dialog', () => {
}

const wrapper = mount(
<DialogWrapTest visible forceRender>
<DialogWrapTest>
<div>Show dialog with forceRender and visible is true</div>
</DialogWrapTest>,
);
Expand All @@ -331,7 +335,7 @@ describe('dialog', () => {
const modalRender = mount(
<Dialog
visible
modalRender={node =>
modalRender={(node: React.ReactElement) =>
cloneElement(node, { ...node.props, style: { background: '#1890ff' } })
}
/>,
Expand All @@ -350,4 +354,24 @@ describe('dialog', () => {
expect(wrapper.find('.rc-dialog').props().style.height).toEqual(903);
});
});

it('should not re-render when visible changed', () => {
let renderTimes = 0;
const RenderChecker = () => {
renderTimes += 1;
return null;
};

const wrapper = mount(
<Dialog visible>
<RenderChecker />
</Dialog>,
);

expect(renderTimes).toEqual(1);

// Hidden should not trigger render
wrapper.setProps({ visible: false });
expect(renderTimes).toEqual(1);
});
});

1 comment on commit cba467a

@vercel
Copy link

@vercel vercel bot commented on cba467a Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.