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"
/>
`;
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 rtlTest from '../../../tests/shared/rtlTest';
import useBreakpoint from '../hooks/useBreakpoint';
import ResponsiveObserve from '../../_util/responsiveObserve';
describe('Grid', () => {
mountTest(Row);
@ -24,7 +25,12 @@ describe('Grid', () => {
it('when typeof gutter is object', () => {
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', () => {
@ -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', () => {
const wrapper = mount(
const wrapper = render(
<Row
gutter={[
{ xs: 8, sm: 16, md: 24, lg: 32, xl: 40 },
@ -48,10 +59,7 @@ describe('Grid', () => {
]}
/>,
);
wrapper.setState({
screens: { md: true, lg: true, xl: true },
});
expect(wrapper.instance().getGutter()).toEqual([40, 400]);
expect(wrapper).toMatchSnapshot();
});
it('renders wrapped Col correctly', () => {
@ -68,10 +76,10 @@ describe('Grid', () => {
});
it('when component has been unmounted, componentWillUnmount should be called', () => {
const wrapper = mount(<Row />);
const willUnmount = jest.spyOn(wrapper.instance(), 'componentWillUnmount');
const Unmount = jest.spyOn(ResponsiveObserve, 'unsubscribe');
const wrapper = mount(<Row gutter={{ xs: 20 }} />);
wrapper.unmount();
expect(willUnmount).toHaveBeenCalled();
expect(Unmount).toHaveBeenCalled();
});
it('should work correct when gutter is object', () => {

View File

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

View File

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