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 interface LocaleReceiverProps<C extends LocaleComponentName = LocaleComponentName> {
componentName: C;
componentName?: C;
defaultLocale?: Locale[C] | (() => Locale[C]);
children: (
locale: NonNullable<Locale[C]>,
localeCode: string,
fullLocale: Locale,
) => React.ReactNode;
) => React.ReactElement;
}
export default class LocaleReceiver<
C extends LocaleComponentName = LocaleComponentName,
> extends React.Component<LocaleReceiverProps<C>> {
static defaultProps = {
componentName: 'global',
};
const LocaleReceiver = <C extends LocaleComponentName = LocaleComponentName>(
props: LocaleReceiverProps<C>,
) => {
const { componentName = 'global' as C, defaultLocale, children } = props;
const antLocale = React.useContext<LocaleContextProps | undefined>(LocaleContext);
static contextType = LocaleContext;
context: LocaleContextProps;
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] : {};
const getLocale = React.useMemo<NonNullable<Locale[C]>>(() => {
const locale = defaultLocale || defaultLocaleData[componentName];
const localeFromContext = antLocale?.[componentName] ?? {};
return {
...(locale instanceof Function ? locale() : locale),
...(localeFromContext || {}),
};
}
}, [componentName, defaultLocale, antLocale]);
getLocaleCode() {
const antLocale = this.context;
const getLocaleCode = React.useMemo<string>(() => {
const localeCode = antLocale && antLocale.locale;
// Had use LocaleProvide but didn't set locale
if (antLocale && antLocale.exist && !localeCode) {
return defaultLocaleData.locale;
}
return localeCode;
}
return localeCode!;
}, [antLocale]);
render() {
return this.props.children(this.getLocale(), this.getLocaleCode(), this.context);
}
}
return children(getLocale, getLocaleCode, antLocale!);
};
export function useLocaleReceiver<T extends LocaleComponentName>(
componentName: T,
defaultLocale?: Locale[T] | Function,
): [Locale[T]] {
const antLocale = React.useContext(LocaleContext);
export default LocaleReceiver;
const componentLocale = React.useMemo(() => {
const locale = defaultLocale || defaultLocaleData[componentName || 'global'];
const localeFromContext = componentName && antLocale ? antLocale[componentName] : {};
export const useLocaleReceiver = <C extends LocaleComponentName = LocaleComponentName>(
componentName: C,
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 {
...(typeof locale === 'function' ? (locale as Function)() : locale),
...(typeof locale === 'function' ? locale() : locale),
...(localeFromContext || {}),
};
}, [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 { render, fireEvent } from '../../../tests/utils';
import LocaleProvider, { ANT_MARK } from '..';
import React, { memo, useContext } from 'react';
import { fireEvent, pureRender } from '../../../tests/utils';
import LocaleProvider from '..';
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.
const CacheInner = memo(() => {
const countRef = useRef(0);
countRef.current++;
const CacheInner: React.FC = memo(() => {
innerCount++;
// subscribe locale context
useContext(LocaleContext);
return (
<div>
Child Rendering Count: <span id="child_count">{countRef.current}</span>
</div>
);
return null;
});
const CacheOuter: React.FC = () => {
// We use 'useState' here in order to trigger parent component rendering.
const [count, setCount] = useState(1);
const handleClick = () => {
setCount(count + 1);
};
// During each rendering phase, the cached context value returned from method 'LocaleProvider#getMemoizedContextValue' will take effect.
// So 'CacheInner' component won't rerender.
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>
);
};
const CacheOuter: React.FC = memo(() => (
<>
<button type="button" onClick={handleClick} id="parent_btn">
Click
</button>
<LocaleProvider locale={{ locale: 'locale' }}>
<CacheInner />
</LocaleProvider>
</>
));
it("Rendering on LocaleProvider won't trigger rendering on child component.", () => {
const { container } = render(<CacheOuter />);
fireEvent.click(container.querySelector('#parent_btn')!);
expect(container.querySelector('#parent_count')?.innerHTML).toBe('2');
// child component won't rerender
expect(container.querySelector('#child_count')?.innerHTML).toBe('1');
const { container } = pureRender(<CacheOuter />);
expect(outerCount).toBe(0);
expect(innerCount).toBe(1);
fireEvent.click(container.querySelector('#parent_btn')!);
expect(container.querySelector('#parent_count')?.innerHTML).toBe('3');
// child component won't rerender
expect(container.querySelector('#child_count')?.innerHTML).toBe('1');
expect(outerCount).toBe(1);
expect(innerCount).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 { Modal } from '../..';
import { sleep, render, fireEvent } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import zhCN from '../zh_CN';
class Demo extends React.Component<{ type: string }> {
static defaultProps = {};
componentDidMount() {
if (this.props.type === 'dashboard') {
const Demo: React.FC<{ type: string }> = ({ type }) => {
useEffect(() => {
if (type === 'dashboard') {
Modal.confirm({ title: 'Hello World!' });
}
}
render() {
return <div>{this.props.type}</div>;
}
}
}, []);
return null;
};
describe('Locale Provider demo', () => {
it('change type', async () => {

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

@ -191,7 +191,7 @@ const App: React.FC = () => (
describe('Locale Provider', () => {
mountTest(() => (
<LocaleProvider>
<LocaleProvider locale={null as unknown as Locale}>
<div />
</LocaleProvider>
));
@ -237,7 +237,7 @@ describe('Locale Provider', () => {
it('set moment locale when locale changes', () => {
const Test: React.FC<{ locale?: Locale }> = ({ locale }) => (
<LocaleProvider locale={locale}>
<LocaleProvider locale={locale!}>
<div>
<DatePicker defaultValue={moment()} open />
</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 * as React from 'react';
import warning from '../_util/warning';
import type { PickerLocale as DatePickerLocale } from '../date-picker/generatePicker';
import type { TransferLocale as TransferLocaleForEmpty } from '../empty';
import type { ModalLocale } from '../modal/locale';
@ -12,6 +10,7 @@ import type { PopconfirmLocale } from '../popconfirm/PurePanel';
import type { TableLocale } from '../table/interface';
import type { TransferLocale } from '../transfer';
import type { UploadLocale } from '../upload/interface';
import type { LocaleContextProps } from './context';
import LocaleContext from './context';
export const ANT_MARK = 'internalMark';
@ -54,45 +53,32 @@ export interface LocaleProviderProps {
_ANT_MARK__?: string;
}
export default class LocaleProvider extends React.Component<LocaleProviderProps, any> {
static defaultProps = {
locale: {},
};
constructor(props: LocaleProviderProps) {
super(props);
changeConfirmLocale(props.locale && props.locale.Modal);
const LocaleProvider: React.FC<LocaleProviderProps> = props => {
const { locale = {} as Locale, children, _ANT_MARK__ } = props;
if (process.env.NODE_ENV !== 'production') {
warning(
props._ANT_MARK__ === ANT_MARK,
_ANT_MARK__ === ANT_MARK,
'LocaleProvider',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead: http://u.ant.design/locale',
);
}
componentDidMount() {
changeConfirmLocale(this.props.locale && this.props.locale.Modal);
}
React.useEffect(() => {
changeConfirmLocale(locale && locale.Modal);
return () => {
changeConfirmLocale();
};
}, [locale]);
componentDidUpdate(prevProps: LocaleProviderProps) {
const { locale } = this.props;
if (prevProps.locale !== locale) {
changeConfirmLocale(locale && locale.Modal);
}
}
const getMemoizedContextValue = React.useMemo<LocaleContextProps>(
() => ({ ...locale, exist: true }),
[locale],
);
componentWillUnmount() {
changeConfirmLocale();
}
getMemoizedContextValue = memoizeOne((localeValue: Locale): Locale & { exist?: boolean } => ({
...localeValue,
exist: true,
}));
return (
<LocaleContext.Provider value={getMemoizedContextValue}>{children}</LocaleContext.Provider>
);
};
render() {
const { locale, children } = this.props;
const contextValue = this.getMemoizedContextValue(locale);
return <LocaleContext.Provider value={contextValue}>{children}</LocaleContext.Provider>;
}
}
export default LocaleProvider;

Loading…
Cancel
Save