Skip to content

Commit 01dadae

Browse files
committed
refactor: replace useMergedState with useControlledState for improved state management
- Updated multiple components to utilize useControlledState instead of useMergedState for better control over state updates. - Refactored state handling in Card, CheckCard, and various form components to enhance performance and maintainability. - Adjusted callback functions to ensure proper state synchronization with parent components. - Improved overall code readability and consistency across the codebase.
1 parent f816b9f commit 01dadae

File tree

39 files changed

+830
-292
lines changed

39 files changed

+830
-292
lines changed

src/card/components/Card/index.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { RightOutlined } from '@ant-design/icons';
2-
import { omit, useMergedState } from '@rc-component/util';
2+
import { omit, useControlledState } from '@rc-component/util';
33
import { ConfigProvider, Grid, Tabs } from 'antd';
44
import { clsx } from 'clsx';
5-
import React, { useContext } from 'react';
5+
import React, { useCallback, useContext } from 'react';
66
import { LabelIconTip } from '../../../utils';
77
import type { Breakpoint, CardProps, Gutter } from '../../typing';
88
import Actions from '../Actions';
@@ -61,10 +61,23 @@ const Card = React.forwardRef((props: CardProps, ref: any) => {
6161
xxl: false,
6262
};
6363

64-
const [collapsed, setCollapsed] = useMergedState<boolean>(defaultCollapsed, {
65-
value: controlCollapsed,
66-
onChange: onCollapse,
67-
});
64+
const [collapsed, setCollapsedInner] = useControlledState<boolean>(
65+
defaultCollapsed,
66+
controlCollapsed,
67+
);
68+
const setCollapsed = useCallback(
69+
(updater: boolean | ((prev: boolean) => boolean)) => {
70+
setCollapsedInner((prev) => {
71+
const next =
72+
typeof updater === 'function'
73+
? (updater as (p: boolean) => boolean)(prev)
74+
: updater;
75+
onCollapse?.(next);
76+
return next;
77+
});
78+
},
79+
[onCollapse],
80+
);
6881

6982
// 顺序决定如何进行响应式取值,按最大响应值依次取值,请勿修改。
7083
const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];

src/card/components/CheckCard/Group.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { RightOutlined } from '@ant-design/icons';
2-
import { omit, useMergedState } from '@rc-component/util';
2+
import { omit, useControlledState } from '@rc-component/util';
33
import { ConfigProvider, Skeleton } from 'antd';
44
import { clsx } from 'clsx';
55
import React, {
@@ -317,12 +317,27 @@ const CheckCardGroup: React.FC<CheckCardGroupProps> = (props) => {
317317
'size',
318318
]);
319319

320-
const [stateValue, setStateValue] = useMergedState<
320+
const [stateValue, setStateValueInner] = useControlledState<
321321
CheckCardValueType[] | CheckCardValueType | undefined
322-
>(props.defaultValue, {
323-
value: props.value,
324-
onChange: props.onChange,
325-
});
322+
>(props.defaultValue, props.value);
323+
324+
const setStateValue = useCallback(
325+
(
326+
updater:
327+
| CheckGroupValueType
328+
| ((prev: CheckGroupValueType) => CheckGroupValueType),
329+
) => {
330+
setStateValueInner((prev) => {
331+
const next =
332+
typeof updater === 'function'
333+
? (updater as (p: CheckGroupValueType) => CheckGroupValueType)(prev)
334+
: updater;
335+
onChange?.(next);
336+
return next;
337+
});
338+
},
339+
[onChange],
340+
);
326341

327342
const registerValueMap = useRef<Map<CheckCardValueType, any>>(new Map());
328343

src/card/components/CheckCard/index.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useMergedState } from '@rc-component/util';
1+
import { useControlledState } from '@rc-component/util';
22
import { Avatar, ConfigProvider } from 'antd';
33
import { clsx } from 'clsx';
44
import type { MouseEventHandler } from 'react';
5-
import React, { useContext, useEffect, useMemo } from 'react';
5+
import React, { useCallback, useContext, useEffect, useMemo } from 'react';
66
import ProCardActions from '../Actions';
77
import type { CheckCardGroupProps } from './Group';
88
import CheckCardGroup, { CardLoading, CheckCardGroupConnext } from './Group';
@@ -169,12 +169,22 @@ export interface CheckCardState {
169169
const CheckCard: React.FC<CheckCardProps> & {
170170
Group: typeof CheckCardGroup;
171171
} = (props) => {
172-
const [stateChecked, setStateChecked] = useMergedState<boolean>(
172+
const [stateChecked, setStateCheckedInner] = useControlledState<boolean>(
173173
props.defaultChecked || false,
174-
{
175-
value: props.checked,
176-
onChange: props.onChange,
174+
props.checked,
175+
);
176+
const setStateChecked = useCallback(
177+
(updater: boolean | ((prev: boolean) => boolean)) => {
178+
setStateCheckedInner((prev) => {
179+
const next =
180+
typeof updater === 'function'
181+
? (updater as (p: boolean) => boolean)(prev)
182+
: updater;
183+
props.onChange?.(next);
184+
return next;
185+
});
177186
},
187+
[props.onChange],
178188
);
179189
const checkCardGroup = useContext(CheckCardGroupConnext);
180190
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);

src/descriptions/useFetchData.tsx

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useMergedState } from '@rc-component/util';
2-
import { useEffect } from 'react';
1+
import { useControlledState } from '@rc-component/util';
2+
import { useCallback, useEffect } from 'react';
33

44
export type RequestData<T = any> = {
55
data?: T;
@@ -34,16 +34,52 @@ const useFetchData = <T extends RequestData>(
3434
defaultDataSource,
3535
onDataSourceChange,
3636
} = options || {};
37-
const [entity, setEntity] = useMergedState<T['data']>(defaultDataSource, {
38-
value: dataSource,
39-
onChange: onDataSourceChange,
40-
});
41-
const [loading, setLoading] = useMergedState<boolean | undefined>(
37+
const [entity, setEntityInner] = useControlledState<T['data']>(
38+
defaultDataSource,
39+
dataSource,
40+
);
41+
const setEntity = useCallback(
42+
(
43+
updater:
44+
| T['data']
45+
| ((prev: T['data']) => T['data']),
46+
) => {
47+
setEntityInner((prev) => {
48+
const next =
49+
typeof updater === 'function'
50+
? (updater as (p: T['data']) => T['data'])(prev)
51+
: updater;
52+
onDataSourceChange?.(next);
53+
return next;
54+
});
55+
},
56+
[onDataSourceChange],
57+
);
58+
const [loading, setLoadingInner] = useControlledState<boolean | undefined>(
59+
options?.loading,
4260
options?.loading,
43-
{
44-
value: options?.loading,
45-
onChange: options?.onLoadingChange,
61+
);
62+
const setLoading = useCallback(
63+
(
64+
updater:
65+
| boolean
66+
| undefined
67+
| ((prev: boolean | undefined) => boolean | undefined),
68+
) => {
69+
setLoadingInner((prev) => {
70+
const next =
71+
typeof updater === 'function'
72+
? (
73+
updater as (
74+
p: boolean | undefined,
75+
) => boolean | undefined
76+
)(prev)
77+
: updater;
78+
options?.onLoadingChange?.(next);
79+
return next;
80+
});
4681
},
82+
[options?.onLoadingChange],
4783
);
4884

4985
const updateDataAndLoading = (data: T) => {

src/field/components/DigitRange/index.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useMergedState } from '@rc-component/util';
1+
import { useControlledState } from '@rc-component/util';
22
import { Input, InputNumber, Space } from 'antd';
3-
import React, { useRef } from 'react';
3+
import React, { useCallback, useRef } from 'react';
44
import { proTheme, useIntl } from '../../../provider';
55
import type { ProFieldFC } from '../../PureProField';
66

@@ -37,10 +37,30 @@ const FieldDigitRange: ProFieldFC<FieldDigitRangeProps> = (
3737
const intl = useIntl();
3838

3939
const { token } = proTheme.useToken();
40-
const [valuePair, setValuePair] = useMergedState(() => defaultValue, {
41-
value: value,
42-
onChange: onChange,
43-
});
40+
const [valuePair, setValuePairInner] = useControlledState(
41+
() => defaultValue,
42+
value,
43+
);
44+
const setValuePair = useCallback(
45+
(
46+
updater:
47+
| ValuePair
48+
| undefined
49+
| ((prev: ValuePair | undefined) => ValuePair | undefined),
50+
) => {
51+
setValuePairInner((prev) => {
52+
const next =
53+
typeof updater === 'function'
54+
? (updater as (p: ValuePair | undefined) => ValuePair | undefined)(
55+
prev,
56+
)
57+
: updater;
58+
onChange?.(next);
59+
return next;
60+
});
61+
},
62+
[onChange],
63+
);
4464
const valuePairRef = useRef(valuePair);
4565

4666
if (type === 'read') {

src/field/components/Money/index.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { omit, useMergedState } from '@rc-component/util';
1+
import { omit, useControlledState } from '@rc-component/util';
22
import type { InputNumberProps } from 'antd';
33
import { InputNumber, Popover } from 'antd';
44
import React, { useCallback, useMemo } from 'react';
@@ -231,10 +231,23 @@ const InputNumberPopover = React.forwardRef<
231231
},
232232
ref,
233233
) => {
234-
const [value, onChange] = useMergedState<any>(() => rest.defaultValue, {
235-
value: rest.value,
236-
onChange: rest.onChange,
237-
});
234+
const [value, setValueInner] = useControlledState<any>(
235+
() => rest.defaultValue,
236+
rest.value,
237+
);
238+
const onChange = useCallback(
239+
(updater: any | ((prev: any) => any)) => {
240+
setValueInner((prev) => {
241+
const next =
242+
typeof updater === 'function'
243+
? (updater as (p: any) => any)(prev)
244+
: updater;
245+
rest.onChange?.(next);
246+
return next;
247+
});
248+
},
249+
[rest.onChange],
250+
);
238251

239252
/**
240253
* 如果content 存在要根据 content 渲染一下

src/field/components/Password/index.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons';
2-
import { useMergedState } from '@rc-component/util';
2+
import { useControlledState } from '@rc-component/util';
33
import { Input, Space } from 'antd';
4-
import React from 'react';
4+
import React, { useCallback } from 'react';
55
import { useIntl } from '../../../provider';
66
import type { ProFieldFC } from '../../PureProField';
77

@@ -20,10 +20,23 @@ const FieldPassword: ProFieldFC<{
2020
) => {
2121
const intl = useIntl();
2222

23-
const [open, setOpen] = useMergedState<boolean>(() => rest.open || false, {
24-
value: rest.open,
25-
onChange: rest.onOpenChange,
26-
});
23+
const [open, setOpenInner] = useControlledState<boolean>(
24+
() => rest.open || false,
25+
rest.open,
26+
);
27+
const setOpen = useCallback(
28+
(updater: boolean | ((prev: boolean) => boolean)) => {
29+
setOpenInner((prev) => {
30+
const next =
31+
typeof updater === 'function'
32+
? (updater as (p: boolean) => boolean)(prev)
33+
: updater;
34+
rest.onOpenChange?.(next);
35+
return next;
36+
});
37+
},
38+
[rest.onOpenChange],
39+
);
2740

2841
if (mode === 'read') {
2942
let dom = <>-</>;

src/field/components/TreeSelect/index.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { useMergedState } from '@rc-component/util';
1+
import { useControlledState } from '@rc-component/util';
22
import type { RadioGroupProps, TreeSelectProps } from 'antd';
33
import { ConfigProvider, Spin, TreeSelect } from 'antd';
44
import { clsx } from 'clsx';
55
import React, {
6+
useCallback,
67
useContext,
78
useImperativeHandle,
89
useMemo,
@@ -63,12 +64,21 @@ const FieldTreeSelect: ProFieldFC<GroupProps> = (
6364
defaultKeyWords: propsSearchValue,
6465
});
6566

66-
const [searchValue, setSearchValue] = useMergedState<string | undefined>(
67-
undefined,
68-
{
69-
onChange: onSearch as any,
70-
value: propsSearchValue,
67+
const [searchValue, setSearchValueInner] = useControlledState<
68+
string | undefined
69+
>(undefined, propsSearchValue);
70+
const setSearchValue = useCallback(
71+
(updater: string | undefined | ((prev: string | undefined) => string | undefined)) => {
72+
setSearchValueInner((prev) => {
73+
const next =
74+
typeof updater === 'function'
75+
? (updater as (p: string | undefined) => string | undefined)(prev)
76+
: updater;
77+
(onSearch as (v?: string) => void)?.(next);
78+
return next;
79+
});
7180
},
81+
[onSearch],
7282
);
7383

7484
useImperativeHandle(ref, () => ({

src/form/BaseForm/BaseForm.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
set as namePathSet,
55
omit,
66
set,
7-
useMergedState,
7+
useControlledState,
88
warning,
99
} from '@rc-component/util';
1010
import { useUrlSearchParams } from '@umijs/use-params';
@@ -14,6 +14,7 @@ import type { NamePath } from 'antd/lib/form/interface';
1414
import { clsx } from 'clsx';
1515
import type dayjs from 'dayjs';
1616
import React, {
17+
useCallback,
1718
useContext,
1819
useEffect,
1920
useImperativeHandle,
@@ -670,10 +671,23 @@ export function BaseForm<T = Record<string, any>, U = Record<string, any>>(
670671
...propRest
671672
} = props;
672673
const formRef = useRef<ProFormRef<any>>({} as any);
673-
const [loading, setLoading] = useMergedState<boolean>(false, {
674-
onChange: onLoadingChange,
675-
value: propsLoading,
676-
});
674+
const [loading, setLoadingInner] = useControlledState<boolean>(
675+
false,
676+
propsLoading,
677+
);
678+
const setLoading = useCallback(
679+
(updater: boolean | ((prev: boolean) => boolean)) => {
680+
setLoadingInner((prev) => {
681+
const next =
682+
typeof updater === 'function'
683+
? (updater as (p: boolean) => boolean)(prev)
684+
: updater;
685+
onLoadingChange?.(next);
686+
return next;
687+
});
688+
},
689+
[onLoadingChange],
690+
);
677691

678692
const [urlSearch, setUrlSearch] = useUrlSearchParams(
679693
{},

0 commit comments

Comments
 (0)