fix: Invalid hook dependency array in Sider and Row (#33804)

This commit is contained in:
Di Wu 2022-01-21 18:39:47 +08:00 committed by GitHub
parent 75adc23fe3
commit fe23374871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 6 deletions

View File

@ -0,0 +1,48 @@
import React, { memo, useRef, useState, useContext } from 'react';
import { mount } from 'enzyme';
import Row from '../row';
import RowContext from '../RowContext';
const CacheInner = memo(() => {
const countRef = useRef(0);
countRef.current++;
useContext(RowContext);
return (
<div>
Child Rendering Count: <span id="child_count">{countRef.current}</span>
</div>
);
});
const CacheOuter = () => {
const [count, setCount] = useState(1);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<button type="button" onClick={handleClick} id="parent_btn">
Click
</button>
Parent Rendering Count: <span id="parent_count">{count}</span>
<Row>
<CacheInner />
</Row>
</div>
);
};
it('Cached RowContext is working', () => {
const wrapper = mount(<CacheOuter />);
const childCount = wrapper.find('#child_count').text();
wrapper.find('#parent_btn').at(0).simulate('click');
expect(wrapper.find('#parent_count').text()).toBe('2');
// child component won't rerender
expect(wrapper.find('#child_count').text()).toBe(childCount);
wrapper.find('#parent_btn').at(0).simulate('click');
expect(wrapper.find('#parent_count').text()).toBe('3');
// child component won't rerender
expect(wrapper.find('#child_count').text()).toBe(childCount);
});

View File

@ -116,11 +116,13 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
rowStyle.marginBottom = verticalGutter; rowStyle.marginBottom = verticalGutter;
} }
const rowContext = React.useMemo(() => ({ gutter: gutters, wrap, supportFlexGap }), [ // "gutters" is a new array in each rendering phase, it'll make 'React.useMemo' effectless.
gutters, // So we deconstruct "gutters" variable here.
wrap, const [gutterH, gutterV] = gutters;
supportFlexGap, const rowContext = React.useMemo(
]); () => ({ gutter: [gutterH, gutterV] as [number, number], wrap, supportFlexGap }),
[gutterH, gutterV, wrap, supportFlexGap],
);
return ( return (
<RowContext.Provider value={rowContext}> <RowContext.Provider value={rowContext}>

View File

@ -136,7 +136,7 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>(
mql?.removeListener(responsiveHandler); mql?.removeListener(responsiveHandler);
} }
}; };
}, []); }, [breakpoint]); // in order to accept dynamic 'breakpoint' property, we need to add 'breakpoint' into dependency array.
useEffect(() => { useEffect(() => {
const uniqueId = generateId('ant-sider-'); const uniqueId = generateId('ant-sider-');

View File

@ -0,0 +1,47 @@
import React, { useState } from 'react';
import { mount } from 'enzyme';
import Sider from '../Sider';
const Content = () => {
const [breakpoint, setBreakpoint] = useState('sm');
const toggleBreakpoint = () => {
if (breakpoint === 'sm') {
setBreakpoint('lg');
} else {
setBreakpoint('sm');
}
};
return (
<Sider breakpoint={breakpoint as any}>
<button type="button" id="toggle" onClick={toggleBreakpoint}>
Toggle
</button>
</Sider>
);
};
it('Dynamic breakpoint in Sider component', () => {
const add = jest.fn();
const remove = jest.fn();
jest.spyOn(window, 'matchMedia').mockReturnValue({
matches: true,
addEventListener: add,
removeEventListener: remove,
} as any);
const wrapper = mount(<Content />);
const newMatch = window.matchMedia as jest.Mock;
// subscribe at first
expect(newMatch.mock.calls.length).toBe(1);
expect(add.mock.calls.length).toBe(1);
expect(remove.mock.calls.length).toBe(0);
wrapper.find('#toggle').at(0).simulate('click');
// unsubscribe then subscribe again
expect(newMatch.mock.calls.length).toBe(2);
expect(add.mock.calls.length).toBe(2);
expect(remove.mock.calls.length).toBe(1);
jest.restoreAllMocks();
});