Skip to content

Commit 1c979b4

Browse files
authored
refactor: useMergeState instead of effect sync (#261)
* refactor: simplify controlled value * test: update test case
1 parent c8e7699 commit 1c979b4

File tree

5 files changed

+170
-161
lines changed

5 files changed

+170
-161
lines changed

docs/example/basic.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@ import ColorPicker, { Color } from '@rc-component/color-picker';
22
import React, { useState } from 'react';
33
import '../../assets/index.less';
44

5+
let start = true;
6+
57
export default () => {
6-
const [value, setValue] = useState(new Color('#163cff'));
8+
const [value, setValue] = useState(new Color('rgba(255,0,0,0)'));
79

810
return (
911
<>
1012
<ColorPicker
11-
color={value}
13+
value={value}
1214
onChange={nextValue => {
13-
console.log('onChange', nextValue.toHsbString(), nextValue);
14-
setValue(nextValue);
15+
let proxyValue = nextValue;
16+
17+
if (start) {
18+
start = false;
19+
proxyValue = nextValue.setA(1);
20+
}
21+
22+
setValue(proxyValue);
1523
}}
1624
/>
1725
<br />

src/ColorPicker.tsx

Lines changed: 130 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const HUE_COLORS = [
4141
},
4242
];
4343

44-
export interface ColorPickerProps extends BaseColorPickerProps {
44+
export interface ColorPickerProps extends Omit<BaseColorPickerProps, 'color'> {
4545
value?: ColorGenInput;
4646
defaultValue?: ColorGenInput;
4747
className?: string;
@@ -53,130 +53,138 @@ export interface ColorPickerProps extends BaseColorPickerProps {
5353
components?: Components;
5454
}
5555

56-
export default forwardRef<HTMLDivElement, ColorPickerProps>((props, ref) => {
57-
const {
58-
value,
59-
defaultValue,
60-
prefixCls = ColorPickerPrefixCls,
61-
onChange,
62-
onChangeComplete,
63-
className,
64-
style,
65-
panelRender,
66-
disabledAlpha = false,
67-
disabled = false,
68-
components,
69-
} = props;
70-
71-
// ========================== Components ==========================
72-
const [Slider] = useComponent(components);
73-
74-
// ============================ Color =============================
75-
const [colorValue, setColorValue] = useColorState(defaultColor, {
76-
value,
77-
defaultValue,
78-
});
79-
const alphaColor = useMemo(
80-
() => colorValue.setA(1).toRgbString(),
81-
[colorValue],
82-
);
83-
84-
// ============================ Events ============================
85-
const handleChange: BaseColorPickerProps['onChange'] = (data, type) => {
86-
if (!value) {
87-
setColorValue(data);
88-
}
89-
onChange?.(data, type);
90-
};
91-
92-
// Convert
93-
const getHueColor = (hue: number) => new Color(colorValue.setHue(hue));
94-
95-
const getAlphaColor = (alpha: number) =>
96-
new Color(colorValue.setA(alpha / 100));
97-
98-
// Slider change
99-
const onHueChange = (hue: number) => {
100-
handleChange(getHueColor(hue), 'hue');
101-
};
102-
103-
const onAlphaChange = (alpha: number) => {
104-
handleChange(getAlphaColor(alpha), 'alpha');
105-
};
106-
107-
// Complete
108-
const onHueChangeComplete = (hue: number) => {
109-
if (onChangeComplete) {
110-
onChangeComplete(getHueColor(hue));
111-
}
112-
};
113-
114-
const onAlphaChangeComplete = (alpha: number) => {
115-
if (onChangeComplete) {
116-
onChangeComplete(getAlphaColor(alpha));
117-
}
118-
};
119-
120-
// ============================ Render ============================
121-
const mergeCls = classNames(`${prefixCls}-panel`, className, {
122-
[`${prefixCls}-panel-disabled`]: disabled,
123-
});
124-
125-
const sharedSliderProps = {
126-
prefixCls,
127-
disabled,
128-
color: colorValue,
129-
};
130-
131-
const defaultPanel = (
132-
<>
133-
<Picker
134-
onChange={handleChange}
135-
{...sharedSliderProps}
136-
onChangeComplete={onChangeComplete}
137-
/>
138-
<div className={`${prefixCls}-slider-container`}>
139-
<div
140-
className={classNames(`${prefixCls}-slider-group`, {
141-
[`${prefixCls}-slider-group-disabled-alpha`]: disabledAlpha,
142-
})}
143-
>
144-
<Slider
145-
{...sharedSliderProps}
146-
type="hue"
147-
colors={HUE_COLORS}
148-
min={0}
149-
max={359}
150-
value={colorValue.getHue()}
151-
onChange={onHueChange}
152-
onChangeComplete={onHueChangeComplete}
153-
/>
154-
{!disabledAlpha && (
56+
const ColorPicker = forwardRef<HTMLDivElement, ColorPickerProps>(
57+
(props, ref) => {
58+
const {
59+
value,
60+
defaultValue,
61+
prefixCls = ColorPickerPrefixCls,
62+
onChange,
63+
onChangeComplete,
64+
className,
65+
style,
66+
panelRender,
67+
disabledAlpha = false,
68+
disabled = false,
69+
components,
70+
} = props;
71+
72+
// ========================== Components ==========================
73+
const [Slider] = useComponent(components);
74+
75+
// ============================ Color =============================
76+
const [colorValue, setColorValue] = useColorState(
77+
defaultValue || defaultColor,
78+
value,
79+
);
80+
const alphaColor = useMemo(
81+
() => colorValue.setA(1).toRgbString(),
82+
[colorValue],
83+
);
84+
85+
// ============================ Events ============================
86+
const handleChange: BaseColorPickerProps['onChange'] = (data, type) => {
87+
if (!value) {
88+
setColorValue(data);
89+
}
90+
onChange?.(data, type);
91+
};
92+
93+
// Convert
94+
const getHueColor = (hue: number) => new Color(colorValue.setHue(hue));
95+
96+
const getAlphaColor = (alpha: number) =>
97+
new Color(colorValue.setA(alpha / 100));
98+
99+
// Slider change
100+
const onHueChange = (hue: number) => {
101+
handleChange(getHueColor(hue), 'hue');
102+
};
103+
104+
const onAlphaChange = (alpha: number) => {
105+
handleChange(getAlphaColor(alpha), 'alpha');
106+
};
107+
108+
// Complete
109+
const onHueChangeComplete = (hue: number) => {
110+
if (onChangeComplete) {
111+
onChangeComplete(getHueColor(hue));
112+
}
113+
};
114+
115+
const onAlphaChangeComplete = (alpha: number) => {
116+
if (onChangeComplete) {
117+
onChangeComplete(getAlphaColor(alpha));
118+
}
119+
};
120+
121+
// ============================ Render ============================
122+
const mergeCls = classNames(`${prefixCls}-panel`, className, {
123+
[`${prefixCls}-panel-disabled`]: disabled,
124+
});
125+
126+
const sharedSliderProps = {
127+
prefixCls,
128+
disabled,
129+
color: colorValue,
130+
};
131+
132+
const defaultPanel = (
133+
<>
134+
<Picker
135+
onChange={handleChange}
136+
{...sharedSliderProps}
137+
onChangeComplete={onChangeComplete}
138+
/>
139+
<div className={`${prefixCls}-slider-container`}>
140+
<div
141+
className={classNames(`${prefixCls}-slider-group`, {
142+
[`${prefixCls}-slider-group-disabled-alpha`]: disabledAlpha,
143+
})}
144+
>
155145
<Slider
156146
{...sharedSliderProps}
157-
type="alpha"
158-
colors={[
159-
{ percent: 0, color: 'rgba(255, 0, 4, 0)' },
160-
{ percent: 100, color: alphaColor },
161-
]}
147+
type="hue"
148+
colors={HUE_COLORS}
162149
min={0}
163-
max={100}
164-
value={colorValue.a * 100}
165-
onChange={onAlphaChange}
166-
onChangeComplete={onAlphaChangeComplete}
150+
max={359}
151+
value={colorValue.getHue()}
152+
onChange={onHueChange}
153+
onChangeComplete={onHueChangeComplete}
167154
/>
168-
)}
155+
{!disabledAlpha && (
156+
<Slider
157+
{...sharedSliderProps}
158+
type="alpha"
159+
colors={[
160+
{ percent: 0, color: 'rgba(255, 0, 4, 0)' },
161+
{ percent: 100, color: alphaColor },
162+
]}
163+
min={0}
164+
max={100}
165+
value={colorValue.a * 100}
166+
onChange={onAlphaChange}
167+
onChangeComplete={onAlphaChangeComplete}
168+
/>
169+
)}
170+
</div>
171+
<ColorBlock color={colorValue.toRgbString()} prefixCls={prefixCls} />
169172
</div>
170-
<ColorBlock color={colorValue.toRgbString()} prefixCls={prefixCls} />
173+
</>
174+
);
175+
176+
return (
177+
<div className={mergeCls} style={style} ref={ref}>
178+
{typeof panelRender === 'function'
179+
? panelRender(defaultPanel)
180+
: defaultPanel}
171181
</div>
172-
</>
173-
);
174-
175-
return (
176-
<div className={mergeCls} style={style} ref={ref}>
177-
{typeof panelRender === 'function'
178-
? panelRender(defaultPanel)
179-
: defaultPanel}
180-
</div>
181-
);
182-
});
182+
);
183+
},
184+
);
185+
186+
if (process.env.NODE_ENV !== 'production') {
187+
ColorPicker.displayName = 'ColorPicker';
188+
}
189+
190+
export default ColorPicker;

src/components/Slider.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Palette from './Palette';
66

77
import classNames from 'classnames';
88
import { useEvent } from 'rc-util';
9-
import type { Color } from '../color';
9+
import { Color } from '../color';
1010
import { calculateColor, calculateOffset } from '../util';
1111
import Gradient from './Gradient';
1212
import Handler from './Handler';
@@ -71,6 +71,20 @@ const Slider: FC<BaseSliderProps> = props => {
7171
disabledDrag: disabled,
7272
});
7373

74+
const handleColor = React.useMemo(() => {
75+
if (type === 'hue') {
76+
const hsb = color.toHsb();
77+
hsb.s = 1;
78+
hsb.b = 1;
79+
hsb.a = 1;
80+
81+
const lightColor = new Color(hsb);
82+
return lightColor;
83+
}
84+
85+
return color;
86+
}, [color, type]);
87+
7488
// ========================= Gradient =========================
7589
const gradientList = React.useMemo(
7690
() => colors.map(info => `${info.color} ${info.percent}%`),
@@ -92,7 +106,7 @@ const Slider: FC<BaseSliderProps> = props => {
92106
<Transform offset={offset} ref={transformRef}>
93107
<Handler
94108
size="small"
95-
color={color.toHexString()}
109+
color={handleColor.toHexString()}
96110
prefixCls={prefixCls}
97111
/>
98112
</Transform>

src/hooks/useColorState.ts

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,20 @@
1-
import { useEffect, useState } from 'react';
1+
import { useMergedState } from 'rc-util';
2+
import { useMemo } from 'react';
23
import type { Color } from '../color';
34
import type { ColorGenInput } from '../interface';
45
import { generateColor } from '../util';
56

67
type ColorValue = ColorGenInput | undefined;
78

8-
function hasValue(value: ColorValue) {
9-
return value !== undefined;
10-
}
11-
129
const useColorState = (
13-
defaultStateValue: ColorValue,
14-
option: {
15-
defaultValue?: ColorValue;
16-
value?: ColorValue;
17-
},
10+
defaultValue: ColorValue,
11+
value?: ColorValue,
1812
): [Color, React.Dispatch<React.SetStateAction<Color>>] => {
19-
const { defaultValue, value } = option;
20-
const [colorValue, setColorValue] = useState(() => {
21-
let mergeState;
22-
if (hasValue(value)) {
23-
mergeState = value;
24-
} else if (hasValue(defaultValue)) {
25-
mergeState = defaultValue;
26-
} else {
27-
mergeState = defaultStateValue;
28-
}
29-
return generateColor(mergeState);
30-
});
13+
const [mergedValue, setValue] = useMergedState(defaultValue, { value });
3114

32-
useEffect(() => {
33-
if (value) {
34-
setColorValue(generateColor(value));
35-
}
36-
}, [value]);
15+
const color = useMemo(() => generateColor(mergedValue), [mergedValue]);
3716

38-
return [colorValue, setColorValue];
17+
return [color, setValue];
3918
};
4019

4120
export default useColorState;

0 commit comments

Comments
 (0)