refactor(Grid): rewrite with hook (#24976)

* refactor: hooks

* fix: lint

* update hooks

* update ref
This commit is contained in:
zoomdong 2020-07-10 17:38:28 +08:00 committed by GitHub
parent dd8e9f8568
commit a3f1de3293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 62 deletions

View File

@ -41,3 +41,10 @@ exports[`Grid should render Row 1`] = `
class="ant-row" class="ant-row"
/> />
`; `;
exports[`Grid when typeof gutter is object array in large screen 1`] = `
<div
class="ant-row"
style="margin-left:-20px;margin-right:-20px;margin-top:-200px;margin-bottom:200px"
/>
`;

View File

@ -4,6 +4,7 @@ import { Col, Row } from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import useBreakpoint from '../hooks/useBreakpoint'; import useBreakpoint from '../hooks/useBreakpoint';
import ResponsiveObserve from '../../_util/responsiveObserve';
describe('Grid', () => { describe('Grid', () => {
mountTest(Row); mountTest(Row);
@ -24,7 +25,12 @@ describe('Grid', () => {
it('when typeof gutter is object', () => { it('when typeof gutter is object', () => {
const wrapper = mount(<Row gutter={{ xs: 8, sm: 16, md: 24 }} />); const wrapper = mount(<Row gutter={{ xs: 8, sm: 16, md: 24 }} />);
expect(wrapper.instance().getGutter()).toEqual([8, 0]); expect(wrapper.find('div').first().props().style).toEqual(
expect.objectContaining({
marginLeft: -4,
marginRight: -4,
}),
);
}); });
it('when typeof gutter is object array', () => { it('when typeof gutter is object array', () => {
@ -36,11 +42,16 @@ describe('Grid', () => {
]} ]}
/>, />,
); );
expect(wrapper.instance().getGutter()).toEqual([8, 8]); expect(wrapper.find('div').first().props().style).toEqual(
expect.objectContaining({
marginLeft: -4,
marginRight: -4,
}),
);
}); });
it('when typeof gutter is object array in large screen', () => { it('when typeof gutter is object array in large screen', () => {
const wrapper = mount( const wrapper = render(
<Row <Row
gutter={[ gutter={[
{ xs: 8, sm: 16, md: 24, lg: 32, xl: 40 }, { xs: 8, sm: 16, md: 24, lg: 32, xl: 40 },
@ -48,10 +59,7 @@ describe('Grid', () => {
]} ]}
/>, />,
); );
wrapper.setState({ expect(wrapper).toMatchSnapshot();
screens: { md: true, lg: true, xl: true },
});
expect(wrapper.instance().getGutter()).toEqual([40, 400]);
}); });
it('renders wrapped Col correctly', () => { it('renders wrapped Col correctly', () => {
@ -68,10 +76,10 @@ describe('Grid', () => {
}); });
it('when component has been unmounted, componentWillUnmount should be called', () => { it('when component has been unmounted, componentWillUnmount should be called', () => {
const wrapper = mount(<Row />); const Unmount = jest.spyOn(ResponsiveObserve, 'unsubscribe');
const willUnmount = jest.spyOn(wrapper.instance(), 'componentWillUnmount'); const wrapper = mount(<Row gutter={{ xs: 20 }} />);
wrapper.unmount(); wrapper.unmount();
expect(willUnmount).toHaveBeenCalled(); expect(Unmount).toHaveBeenCalled();
}); });
it('should work correct when gutter is object', () => { it('should work correct when gutter is object', () => {

View File

@ -44,9 +44,8 @@ function parseFlex(flex: FlexType): string {
return flex; return flex;
} }
export default class Col extends React.Component<ColProps, {}> { const Col = React.forwardRef<HTMLDivElement, ColProps>((props, ref) => {
renderCol = ({ getPrefixCls, direction }: ConfigConsumerProps) => { const renderCol = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const { props } = this;
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
span, span,
@ -123,7 +122,7 @@ export default class Col extends React.Component<ColProps, {}> {
} }
return ( return (
<div {...others} style={mergedStyle} className={classes}> <div {...others} style={mergedStyle} className={classes} ref={ref}>
{children} {children}
</div> </div>
); );
@ -132,7 +131,9 @@ export default class Col extends React.Component<ColProps, {}> {
); );
}; };
render() { return <ConfigConsumer>{renderCol}</ConfigConsumer>;
return <ConfigConsumer>{this.renderCol}</ConfigConsumer>; });
}
} Col.displayName = 'Col';
export default Col;

View File

@ -20,48 +20,37 @@ export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
prefixCls?: string; prefixCls?: string;
} }
export interface RowState { const Row = React.forwardRef<HTMLDivElement, RowProps>((props, ref) => {
screens: ScreenMap; const [screens, setScreens] = React.useState<ScreenMap>({
} xs: true,
sm: true,
md: true,
lg: true,
xl: true,
xxl: true,
});
const gutterRef = React.useRef<Gutter | [Gutter, Gutter]>();
gutterRef.current = props.gutter;
export default class Row extends React.Component<RowProps, RowState> { React.useEffect(() => {
static defaultProps = { const token = ResponsiveObserve.subscribe(screen => {
gutter: 0, const currentGutter = gutterRef.current || 0;
};
state: RowState = {
screens: {
xs: true,
sm: true,
md: true,
lg: true,
xl: true,
xxl: true,
},
};
token: number;
componentDidMount() {
this.token = ResponsiveObserve.subscribe(screens => {
const { gutter } = this.props;
if ( if (
(!Array.isArray(gutter) && typeof gutter === 'object') || (!Array.isArray(currentGutter) && typeof currentGutter === 'object') ||
(Array.isArray(gutter) && (typeof gutter[0] === 'object' || typeof gutter[1] === 'object')) (Array.isArray(currentGutter) &&
(typeof currentGutter[0] === 'object' || typeof currentGutter[1] === 'object'))
) { ) {
this.setState({ screens }); setScreens(screen);
} }
}); });
} return () => {
ResponsiveObserve.unsubscribe(token);
};
}, []);
componentWillUnmount() { const getGutter = (): [number, number] => {
ResponsiveObserve.unsubscribe(this.token);
}
getGutter(): [number, number] {
const results: [number, number] = [0, 0]; const results: [number, number] = [0, 0];
const { gutter } = this.props; const { gutter = 0 } = props;
const { screens } = this.state;
const normalizedGutter = Array.isArray(gutter) ? gutter : [gutter, 0]; const normalizedGutter = Array.isArray(gutter) ? gutter : [gutter, 0];
normalizedGutter.forEach((g, index) => { normalizedGutter.forEach((g, index) => {
if (typeof g === 'object') { if (typeof g === 'object') {
@ -77,9 +66,9 @@ export default class Row extends React.Component<RowProps, RowState> {
} }
}); });
return results; return results;
} };
renderRow = ({ getPrefixCls, direction }: ConfigConsumerProps) => { const renderRow = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
justify, justify,
@ -88,9 +77,9 @@ export default class Row extends React.Component<RowProps, RowState> {
style, style,
children, children,
...others ...others
} = this.props; } = props;
const prefixCls = getPrefixCls('row', customizePrefixCls); const prefixCls = getPrefixCls('row', customizePrefixCls);
const gutter = this.getGutter(); const gutter = getGutter();
const classes = classNames( const classes = classNames(
prefixCls, prefixCls,
{ {
@ -120,14 +109,16 @@ export default class Row extends React.Component<RowProps, RowState> {
return ( return (
<RowContext.Provider value={{ gutter }}> <RowContext.Provider value={{ gutter }}>
<div {...otherProps} className={classes} style={rowStyle}> <div {...otherProps} className={classes} style={rowStyle} ref={ref}>
{children} {children}
</div> </div>
</RowContext.Provider> </RowContext.Provider>
); );
}; };
render() { return <ConfigConsumer>{renderRow}</ConfigConsumer>;
return <ConfigConsumer>{this.renderRow}</ConfigConsumer>; });
}
} Row.displayName = 'Row';
export default Row;