Browse Source

refactor: CC => FC (#37886)

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* fix: fix

* test: fix test

* fix: fix

* fix: fix

* fix: fix

* test: add test

* fix: fix test
pull/37915/head
lijianan 2 years ago
committed by GitHub
parent
commit
e084de38e8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 65
      components/locale-provider/LocaleReceiver.tsx
  2. 69
      components/locale-provider/__tests__/cached-context.test.tsx
  3. 19
      components/locale-provider/__tests__/config.test.tsx
  4. 4
      components/locale-provider/__tests__/index.test.tsx
  5. 54
      components/locale-provider/index.tsx

65
components/locale-provider/LocaleReceiver.tsx

@ -7,67 +7,58 @@ import defaultLocaleData from './default';
export type LocaleComponentName = Exclude<keyof Locale, 'locale'>; export type LocaleComponentName = Exclude<keyof Locale, 'locale'>;
export interface LocaleReceiverProps<C extends LocaleComponentName = LocaleComponentName> { export interface LocaleReceiverProps<C extends LocaleComponentName = LocaleComponentName> {
componentName: C; componentName?: C;
defaultLocale?: Locale[C] | (() => Locale[C]); defaultLocale?: Locale[C] | (() => Locale[C]);
children: ( children: (
locale: NonNullable<Locale[C]>, locale: NonNullable<Locale[C]>,
localeCode: string, localeCode: string,
fullLocale: Locale, fullLocale: Locale,
) => React.ReactNode; ) => React.ReactElement;
} }
export default class LocaleReceiver< const LocaleReceiver = <C extends LocaleComponentName = LocaleComponentName>(
C extends LocaleComponentName = LocaleComponentName, props: LocaleReceiverProps<C>,
> extends React.Component<LocaleReceiverProps<C>> { ) => {
static defaultProps = { const { componentName = 'global' as C, defaultLocale, children } = props;
componentName: 'global', const antLocale = React.useContext<LocaleContextProps | undefined>(LocaleContext);
};
static contextType = LocaleContext; const getLocale = React.useMemo<NonNullable<Locale[C]>>(() => {
const locale = defaultLocale || defaultLocaleData[componentName];
context: LocaleContextProps; const localeFromContext = antLocale?.[componentName] ?? {};
getLocale(): NonNullable<Locale[C]> {
const { componentName, defaultLocale } = this.props;
const locale = defaultLocale || defaultLocaleData[componentName ?? 'global'];
const antLocale = this.context;
const localeFromContext = componentName && antLocale ? antLocale[componentName] : {};
return { return {
...(locale instanceof Function ? locale() : locale), ...(locale instanceof Function ? locale() : locale),
...(localeFromContext || {}), ...(localeFromContext || {}),
}; };
} }, [componentName, defaultLocale, antLocale]);
getLocaleCode() { const getLocaleCode = React.useMemo<string>(() => {
const antLocale = this.context;
const localeCode = antLocale && antLocale.locale; const localeCode = antLocale && antLocale.locale;
// Had use LocaleProvide but didn't set locale // Had use LocaleProvide but didn't set locale
if (antLocale && antLocale.exist && !localeCode) { if (antLocale && antLocale.exist && !localeCode) {
return defaultLocaleData.locale; return defaultLocaleData.locale;
} }
return localeCode; return localeCode!;
} }, [antLocale]);
render() { return children(getLocale, getLocaleCode, antLocale!);
return this.props.children(this.getLocale(), this.getLocaleCode(), this.context); };
}
}
export function useLocaleReceiver<T extends LocaleComponentName>( export default LocaleReceiver;
componentName: T,
defaultLocale?: Locale[T] | Function,
): [Locale[T]] {
const antLocale = React.useContext(LocaleContext);
const componentLocale = React.useMemo(() => { export const useLocaleReceiver = <C extends LocaleComponentName = LocaleComponentName>(
const locale = defaultLocale || defaultLocaleData[componentName || 'global']; componentName: C,
const localeFromContext = componentName && antLocale ? antLocale[componentName] : {}; defaultLocale?: Locale[C] | (() => Locale[C]),
): [Locale[C]] => {
const antLocale = React.useContext<LocaleContextProps | undefined>(LocaleContext);
const getLocale = React.useMemo<NonNullable<Locale[C]>>(() => {
const locale = defaultLocale || defaultLocaleData[componentName];
const localeFromContext = antLocale?.[componentName] ?? {};
return { return {
...(typeof locale === 'function' ? (locale as Function)() : locale), ...(typeof locale === 'function' ? locale() : locale),
...(localeFromContext || {}), ...(localeFromContext || {}),
}; };
}, [componentName, defaultLocale, antLocale]); }, [componentName, defaultLocale, antLocale]);
return [componentLocale]; return [getLocale];
} };

69
components/locale-provider/__tests__/cached-context.test.tsx

@ -1,51 +1,38 @@
import React, { memo, useContext, useRef, useState } from 'react'; import React, { memo, useContext } from 'react';
import { render, fireEvent } from '../../../tests/utils'; import { fireEvent, pureRender } from '../../../tests/utils';
import LocaleProvider, { ANT_MARK } from '..'; import LocaleProvider from '..';
import LocaleContext from '../context'; import LocaleContext from '../context';
const defaultLocale = { locale: 'locale' }; let innerCount = 0;
let outerCount = 0;
const handleClick = () => {
outerCount++;
};
// we use'memo' here in order to only render inner component while context changed. // we use'memo' here in order to only render inner component while context changed.
const CacheInner = memo(() => { const CacheInner: React.FC = memo(() => {
const countRef = useRef(0); innerCount++;
countRef.current++;
// subscribe locale context // subscribe locale context
useContext(LocaleContext); useContext(LocaleContext);
return ( return null;
<div>
Child Rendering Count: <span id="child_count">{countRef.current}</span>
</div>
);
}); });
const CacheOuter: React.FC = () => { const CacheOuter: React.FC = memo(() => (
// We use 'useState' here in order to trigger parent component rendering. <>
const [count, setCount] = useState(1); <button type="button" onClick={handleClick} id="parent_btn">
const handleClick = () => { Click
setCount(count + 1); </button>
}; <LocaleProvider locale={{ locale: 'locale' }}>
// During each rendering phase, the cached context value returned from method 'LocaleProvider#getMemoizedContextValue' will take effect. <CacheInner />
// So 'CacheInner' component won't rerender. </LocaleProvider>
return ( </>
<div> ));
<button type="button" onClick={handleClick} id="parent_btn">
Click
</button>
Parent Rendering Count: <span id="parent_count">{count}</span>
<LocaleProvider locale={defaultLocale} _ANT_MARK__={ANT_MARK}>
<CacheInner />
</LocaleProvider>
</div>
);
};
it("Rendering on LocaleProvider won't trigger rendering on child component.", () => { it("Rendering on LocaleProvider won't trigger rendering on child component.", () => {
const { container } = render(<CacheOuter />); const { container } = pureRender(<CacheOuter />);
fireEvent.click(container.querySelector('#parent_btn')!); expect(outerCount).toBe(0);
expect(container.querySelector('#parent_count')?.innerHTML).toBe('2'); expect(innerCount).toBe(1);
// child component won't rerender
expect(container.querySelector('#child_count')?.innerHTML).toBe('1');
fireEvent.click(container.querySelector('#parent_btn')!); fireEvent.click(container.querySelector('#parent_btn')!);
expect(container.querySelector('#parent_count')?.innerHTML).toBe('3'); expect(outerCount).toBe(1);
// child component won't rerender expect(innerCount).toBe(1);
expect(container.querySelector('#child_count')?.innerHTML).toBe('1');
}); });

19
components/locale-provider/__tests__/config.test.tsx

@ -1,23 +1,18 @@
import React from 'react'; import React, { useEffect } from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { Modal } from '../..'; import { Modal } from '../..';
import { sleep, render, fireEvent } from '../../../tests/utils'; import { sleep, render, fireEvent } from '../../../tests/utils';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import zhCN from '../zh_CN'; import zhCN from '../zh_CN';
class Demo extends React.Component<{ type: string }> { const Demo: React.FC<{ type: string }> = ({ type }) => {
static defaultProps = {}; useEffect(() => {
if (type === 'dashboard') {
componentDidMount() {
if (this.props.type === 'dashboard') {
Modal.confirm({ title: 'Hello World!' }); Modal.confirm({ title: 'Hello World!' });
} }
} }, []);
return null;
render() { };
return <div>{this.props.type}</div>;
}
}
describe('Locale Provider demo', () => { describe('Locale Provider demo', () => {
it('change type', async () => { it('change type', async () => {

4
components/locale-provider/__tests__/index.test.tsx

@ -191,7 +191,7 @@ const App: React.FC = () => (
describe('Locale Provider', () => { describe('Locale Provider', () => {
mountTest(() => ( mountTest(() => (
<LocaleProvider> <LocaleProvider locale={null as unknown as Locale}>
<div /> <div />
</LocaleProvider> </LocaleProvider>
)); ));
@ -237,7 +237,7 @@ describe('Locale Provider', () => {
it('set moment locale when locale changes', () => { it('set moment locale when locale changes', () => {
const Test: React.FC<{ locale?: Locale }> = ({ locale }) => ( const Test: React.FC<{ locale?: Locale }> = ({ locale }) => (
<LocaleProvider locale={locale}> <LocaleProvider locale={locale!}>
<div> <div>
<DatePicker defaultValue={moment()} open /> <DatePicker defaultValue={moment()} open />
</div> </div>

54
components/locale-provider/index.tsx

@ -1,8 +1,6 @@
import memoizeOne from 'memoize-one';
import type { ValidateMessages } from 'rc-field-form/lib/interface'; import type { ValidateMessages } from 'rc-field-form/lib/interface';
import * as React from 'react'; import * as React from 'react';
import warning from '../_util/warning'; import warning from '../_util/warning';
import type { PickerLocale as DatePickerLocale } from '../date-picker/generatePicker'; import type { PickerLocale as DatePickerLocale } from '../date-picker/generatePicker';
import type { TransferLocale as TransferLocaleForEmpty } from '../empty'; import type { TransferLocale as TransferLocaleForEmpty } from '../empty';
import type { ModalLocale } from '../modal/locale'; import type { ModalLocale } from '../modal/locale';
@ -12,6 +10,7 @@ import type { PopconfirmLocale } from '../popconfirm/PurePanel';
import type { TableLocale } from '../table/interface'; import type { TableLocale } from '../table/interface';
import type { TransferLocale } from '../transfer'; import type { TransferLocale } from '../transfer';
import type { UploadLocale } from '../upload/interface'; import type { UploadLocale } from '../upload/interface';
import type { LocaleContextProps } from './context';
import LocaleContext from './context'; import LocaleContext from './context';
export const ANT_MARK = 'internalMark'; export const ANT_MARK = 'internalMark';
@ -54,45 +53,32 @@ export interface LocaleProviderProps {
_ANT_MARK__?: string; _ANT_MARK__?: string;
} }
export default class LocaleProvider extends React.Component<LocaleProviderProps, any> { const LocaleProvider: React.FC<LocaleProviderProps> = props => {
static defaultProps = { const { locale = {} as Locale, children, _ANT_MARK__ } = props;
locale: {},
};
constructor(props: LocaleProviderProps) {
super(props);
changeConfirmLocale(props.locale && props.locale.Modal);
if (process.env.NODE_ENV !== 'production') {
warning( warning(
props._ANT_MARK__ === ANT_MARK, _ANT_MARK__ === ANT_MARK,
'LocaleProvider', 'LocaleProvider',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead: http://u.ant.design/locale', '`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead: http://u.ant.design/locale',
); );
} }
componentDidMount() { React.useEffect(() => {
changeConfirmLocale(this.props.locale && this.props.locale.Modal); changeConfirmLocale(locale && locale.Modal);
} return () => {
changeConfirmLocale();
};
}, [locale]);
componentDidUpdate(prevProps: LocaleProviderProps) { const getMemoizedContextValue = React.useMemo<LocaleContextProps>(
const { locale } = this.props; () => ({ ...locale, exist: true }),
if (prevProps.locale !== locale) { [locale],
changeConfirmLocale(locale && locale.Modal); );
}
}
componentWillUnmount() { return (
changeConfirmLocale(); <LocaleContext.Provider value={getMemoizedContextValue}>{children}</LocaleContext.Provider>
} );
};
getMemoizedContextValue = memoizeOne((localeValue: Locale): Locale & { exist?: boolean } => ({
...localeValue,
exist: true,
}));
render() { export default LocaleProvider;
const { locale, children } = this.props;
const contextValue = this.getMemoizedContextValue(locale);
return <LocaleContext.Provider value={contextValue}>{children}</LocaleContext.Provider>;
}
}

Loading…
Cancel
Save