Skip to content

Commit f078371

Browse files
authored
fix: onChange.option miss when options change (#528)
* test: Add missing test * fix: Support cache options * fix ts
1 parent fac2efa commit f078371

File tree

3 files changed

+77
-7
lines changed

3 files changed

+77
-7
lines changed

src/generate.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,15 @@ export interface GenerateConfig<OptionsType extends object[]> {
187187
/** Convert single raw value into { label, value } format. Will be called by each value */
188188
getLabeledValue: GetLabeledValue<FlattenOptionsType<OptionsType>>;
189189
filterOptions: FilterOptions<OptionsType>;
190-
findValueOption: (
191-
values: RawValueType[],
192-
options: FlattenOptionsType<OptionsType>,
193-
) => OptionsType;
190+
findValueOption:
191+
| (// Need still support legacy ts api
192+
(values: RawValueType[], options: FlattenOptionsType<OptionsType>) => OptionsType)
193+
| (// New API add prevValueOptions support
194+
(
195+
values: RawValueType[],
196+
options: FlattenOptionsType<OptionsType>,
197+
info?: { prevValueOptions?: OptionsType[] },
198+
) => OptionsType);
194199
/** Check if a value is disabled */
195200
isValueDisabled: (value: RawValueType, options: FlattenOptionsType<OptionsType>) => boolean;
196201
warningProps?: (props: any) => void;
@@ -515,6 +520,9 @@ export default function generateSelector<
515520
}
516521
};
517522

523+
// We need cache options here in case user update the option list
524+
const [prevValueOptions, setPrevValueOptions] = useState([]);
525+
518526
const triggerChange = (newRawValues: RawValueType[]) => {
519527
if (useInternalProps && internalProps.skipTriggerChange) {
520528
return;
@@ -531,7 +539,18 @@ export default function generateSelector<
531539
const outValue: ValueType = (isMultiple ? outValues : outValues[0]) as ValueType;
532540
// Skip trigger if prev & current value is both empty
533541
if (onChange && (mergedRawValue.length !== 0 || outValues.length !== 0)) {
534-
const outOptions = findValueOption(newRawValues, newRawValuesOptions);
542+
const outOptions = findValueOption(newRawValues, newRawValuesOptions, { prevValueOptions });
543+
544+
// We will cache option in case it removed by ajax
545+
setPrevValueOptions(
546+
outOptions.map((option, index) => {
547+
const clone = { ...option };
548+
Object.defineProperty(clone, '_INTERNAL_OPTION_VALUE_', {
549+
get: () => newRawValues[index],
550+
});
551+
return clone;
552+
}),
553+
);
535554

536555
onChange(outValue, isMultiple ? outOptions : outOptions[0]);
537556
}

src/utils/valueUtil.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ function injectPropsWithOption<T>(option: T): T {
9090
export function findValueOption(
9191
values: RawValueType[],
9292
options: FlattenOptionData[],
93+
{ prevValueOptions = [] }: { prevValueOptions?: OptionData[] } = {},
9394
): OptionData[] {
9495
const optionMap: Map<RawValueType, OptionData> = new Map();
9596

@@ -101,7 +102,19 @@ export function findValueOption(
101102
}
102103
});
103104

104-
return values.map(val => injectPropsWithOption(optionMap.get(val)));
105+
return values.map(val => {
106+
let option = optionMap.get(val);
107+
108+
// Fallback to try to find prev options
109+
if (!option) {
110+
option = {
111+
// eslint-disable-next-line no-underscore-dangle
112+
...prevValueOptions.find(opt => opt._INTERNAL_OPTION_VALUE_ === val),
113+
};
114+
}
115+
116+
return injectPropsWithOption(option);
117+
});
105118
}
106119

107120
export const getLabeledValue: GetLabeledValue<FlattenOptionData[]> = (

tests/Multiple.test.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe('Select.Multiple', () => {
121121
expect(wrapper.find('input').props().value).toBe('');
122122
});
123123

124-
it('shouldn\'t separate words when compositing', () => {
124+
it("shouldn't separate words when compositing", () => {
125125
const handleChange = jest.fn();
126126
const handleSelect = jest.fn();
127127
const wrapper = mount(
@@ -391,4 +391,42 @@ describe('Select.Multiple', () => {
391391
toggleOpen(wrapper);
392392
expect(wrapper.find('input').props().value).toEqual('');
393393
});
394+
395+
it('ajax update should keep options', () => {
396+
const onChange = jest.fn();
397+
398+
const wrapper = mount(
399+
<Select
400+
labelInValue
401+
mode="multiple"
402+
options={[{ value: 'light', label: 'Light', option: 2333 }]}
403+
onChange={onChange}
404+
showSearch
405+
/>,
406+
);
407+
408+
// First select
409+
toggleOpen(wrapper);
410+
selectItem(wrapper, 0);
411+
expect(onChange).toHaveBeenCalledWith(
412+
[{ label: 'Light', value: 'light', key: 'light' }],
413+
[{ label: 'Light', value: 'light', option: 2333 }],
414+
);
415+
onChange.mockReset();
416+
417+
// Next select
418+
wrapper.setProps({ options: [{ value: 'bamboo', label: 'Bamboo', option: 444 }] });
419+
toggleOpen(wrapper);
420+
selectItem(wrapper, 0);
421+
expect(onChange).toHaveBeenCalledWith(
422+
[
423+
{ label: 'Light', value: 'light', key: 'light' },
424+
{ label: 'Bamboo', value: 'bamboo', key: 'bamboo' },
425+
],
426+
[
427+
{ label: 'Light', value: 'light', option: 2333 },
428+
{ value: 'bamboo', label: 'Bamboo', option: 444 },
429+
],
430+
);
431+
});
394432
});

0 commit comments

Comments
 (0)