Skip to content

Commit 8f63613

Browse files
committed
Fix for updating selected value when data is changing (#55)
1 parent c86366c commit 8f63613

File tree

2 files changed

+125
-70
lines changed

2 files changed

+125
-70
lines changed

examples/src/components/Tags.js

+62-11
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@ export default class Tags extends Component {
99
value1: ['feature'],
1010
value2: ['feature'],
1111
value3: null,
12+
value10: null,
13+
onChange10: () => {
14+
console.log('onChange');
15+
},
1216
data7: [
1317
{ text: 'bug', id: 1 },
1418
{ text: 'feature', id: 2 },
1519
{ text: 'documents', id: 3 },
1620
{ text: 'discussion', id: 4 },
1721
],
1822
data9: ['bug', 'feature', 'documents', 'discussion'],
23+
data10: [
24+
{ text: 'bug', id: 1 },
25+
{ text: 'feature', id: 2 },
26+
{ text: 'documents', id: 3 },
27+
{ text: 'discussion', id: 4 },
28+
],
1929
placeholder9: 'search by tags',
2030
};
2131
}
@@ -24,7 +34,7 @@ export default class Tags extends Component {
2434
const { value1 } = this.state;
2535
return (
2636
<div>
27-
Update `value`<br/>
37+
Set `bug` `discussion`<br/>
2838
<Select2
2939
multiple
3040
data={['bug', 'feature', 'documents', 'discussion']}
@@ -37,7 +47,7 @@ export default class Tags extends Component {
3747
/>
3848
3949
<button onClick={() => this.setState({ value1: ['bug', 'discussion'] })}>
40-
set `bug` `discussion` value
50+
Update
4151
</button>
4252
</div>
4353
);
@@ -47,7 +57,7 @@ export default class Tags extends Component {
4757
const { value2 } = this.state;
4858
return (
4959
<div>
50-
Update mutated `value`<br/>
60+
Set mutated `documents`<br/>
5161
<Select2
5262
multiple
5363
data={['bug', 'feature', 'documents', 'discussion']}
@@ -66,7 +76,7 @@ export default class Tags extends Component {
6676
this.setState({ value2: items });
6777
}}
6878
>
69-
add `documents` value
79+
Update
7080
</button>
7181
</div>
7282
);
@@ -76,7 +86,7 @@ export default class Tags extends Component {
7686
const { value3 } = this.state;
7787
return (
7888
<div>
79-
Data as an object<br/>
89+
Set `documents`<br/>
8090
<Select2
8191
multiple={false}
8292
data={[
@@ -93,7 +103,7 @@ export default class Tags extends Component {
93103
/>
94104
95105
<button onClick={() => this.setState({ value3: 3 })}>
96-
set `documents` value
106+
Update
97107
</button>
98108
</div>
99109
);
@@ -166,7 +176,7 @@ export default class Tags extends Component {
166176
const { data7 } = this.state;
167177
return (
168178
<div>
169-
Dynamic update data<br/>
179+
Set a new data<br/>
170180
<Select2
171181
defaultValue={1}
172182
data={ data7 }
@@ -184,7 +194,7 @@ export default class Tags extends Component {
184194
],
185195
})}
186196
>
187-
reload data value
197+
Update
188198
</button>
189199
</div>
190200
);
@@ -219,7 +229,7 @@ export default class Tags extends Component {
219229
const { placeholder9, data9 } = this.state;
220230
return (
221231
<div>
222-
Update Options<br/>
232+
Set placeholder and a new data<br/>
223233
<Select2
224234
multiple
225235
data={data9}
@@ -230,8 +240,48 @@ export default class Tags extends Component {
230240
}
231241
/>
232242
233-
<button onClick={() => this.setState({ placeholder9: 'test', data9: ['feature'] })}>
234-
set `placeholder` and `data`
243+
<button onClick={() => this.setState({
244+
placeholder9: 'test',
245+
data9: ['feature'],
246+
})}
247+
>
248+
Update
249+
</button>
250+
</div>
251+
);
252+
}
253+
254+
example10() {
255+
const { value10, data10, onChange10 } = this.state;
256+
return (
257+
<div>
258+
Add a new item and set a new `onChange` event<br/>
259+
<Select2
260+
multiple={false}
261+
data={data10}
262+
defaultValue={ 1 }
263+
value={ value10 }
264+
onChange={onChange10}
265+
options={{
266+
placeholder: 'search by tags',
267+
}}
268+
/>
269+
270+
<button onClick={() => this.setState({
271+
value10: 5,
272+
data10: [
273+
{ text: 'bug', id: 1 },
274+
{ text: 'feature', id: 2 },
275+
{ text: 'documents', id: 3 },
276+
{ text: 'discussion', id: 4 },
277+
{ text: 'tester', id: 5 },
278+
],
279+
onChange10: () => {
280+
console.log('onChange!');
281+
},
282+
})}
283+
>
284+
Update
235285
</button>
236286
</div>
237287
);
@@ -249,6 +299,7 @@ export default class Tags extends Component {
249299
{this.example7()}<br/>
250300
{this.example8()}<br/>
251301
{this.example9()}<br/>
302+
{this.example10()}<br/>
252303
</div>
253304
);
254305
}

src/components/Select2.js

+63-59
Original file line numberDiff line numberDiff line change
@@ -45,109 +45,113 @@ export default class Select2 extends Component {
4545
constructor(props) {
4646
super(props);
4747
this.el = null;
48+
this.forceUpdateValue = false;
4849
}
4950

5051
componentDidMount() {
51-
this.initSelect2();
52+
this.initSelect2(this.props);
53+
this.updateValue();
5254
}
5355

5456
componentWillReceiveProps(nextProps) {
55-
if (this.el) {
56-
this.setValue(this.prepareValue(nextProps.value, this.props.defaultValue));
57-
}
57+
this.updSelect2(nextProps);
5858
}
5959

60-
componentDidUpdate(prevProps) {
61-
if (!shallowEqualFuzzy(prevProps.data, this.props.data)) {
62-
this.destroySelect2(false);
63-
this.initSelect2(false);
64-
} else {
65-
const { options } = this.props;
66-
if (!shallowEqualFuzzy(prevProps.options, options)) {
67-
this.el.select2(this.prepareOptions(options));
68-
}
69-
}
70-
71-
const handlerChanged = e => prevProps[e[1]] !== this.props[e[1]];
72-
73-
if (this.props.events.some(handlerChanged)) {
74-
this.detachEventHandlers();
75-
this.attachEventHandlers();
76-
}
60+
componentDidUpdate() {
61+
this.updateValue();
7762
}
7863

7964
componentWillUnmount() {
8065
this.destroySelect2();
8166
}
8267

83-
setValue(value) {
84-
const elVal = this.props.multiple ? this.el.val() || [] : this.el.val();
85-
if (!shallowEqualFuzzy(elVal, value)) {
86-
this.el.val(value).trigger('change');
68+
initSelect2(props, updateValue = false) {
69+
const { options } = props;
70+
71+
this.el = $(ReactDOM.findDOMNode(this));
72+
// fix for updating selected value when data is changing
73+
if (updateValue) {
74+
this.forceUpdateValue = true;
75+
this.el.off(`change.${namespace}`).val(null).trigger('change');
8776
}
77+
this.el.select2(this.prepareOptions(options));
78+
this.attachEventHandlers(props);
8879
}
8980

90-
prepareValue(value, defaultValue) {
91-
const issetValue = typeof value !== 'undefined' && value !== null;
92-
const issetDefaultValue = typeof defaultValue !== 'undefined';
81+
updSelect2(props) {
82+
const prevProps = this.props;
9383

94-
if (!issetValue && issetDefaultValue) {
95-
return defaultValue;
84+
if (!shallowEqualFuzzy(prevProps.data, props.data)) {
85+
this.destroySelect2(false);
86+
this.initSelect2(props, true);
87+
} else {
88+
const { options } = props;
89+
if (!shallowEqualFuzzy(prevProps.options, options)) {
90+
this.el.select2(this.prepareOptions(options));
91+
}
9692
}
97-
return value;
98-
}
9993

100-
prepareOptions(options) {
101-
const opt = options;
102-
if (typeof opt.dropdownParent === 'string') {
103-
opt.dropdownParent = $(opt.dropdownParent);
94+
const handlerChanged = e => prevProps[e[1]] !== props[e[1]];
95+
if (props.events.some(handlerChanged)) {
96+
this.detachEventHandlers(props);
97+
this.attachEventHandlers(props);
10498
}
105-
return opt;
10699
}
107100

108-
initSelect2(withCallbacks = true) {
109-
if (this.el) { return; }
110-
const { defaultValue, value, options } = this.props;
111-
112-
this.el = $(ReactDOM.findDOMNode(this));
113-
this.el.select2(this.prepareOptions(options));
114-
115-
if (withCallbacks) {
116-
this.attachEventHandlers();
117-
}
101+
updateValue() {
102+
const { value, defaultValue, multiple } = this.props;
103+
const newValue = this.prepareValue(value, defaultValue);
104+
const currentValue = multiple ? this.el.val() || [] : this.el.val();
118105

119-
if (typeof defaultValue === 'undefined' && typeof value !== 'undefined') {
120-
this.setValue(value);
106+
if (!shallowEqualFuzzy(currentValue, newValue) || this.forceUpdateValue) {
107+
this.el.val(newValue).trigger('change');
108+
this.forceUpdateValue = false;
121109
}
122110
}
123111

124112
destroySelect2(withCallbacks = true) {
125-
if (!this.el) { return; }
126-
127113
if (withCallbacks) {
128-
this.detachEventHandlers();
114+
this.detachEventHandlers(this.props);
129115
}
130116

131117
this.el.select2('destroy');
132118
this.el = null;
133119
}
134120

135-
attachEventHandlers() {
136-
this.props.events.forEach(event => {
137-
if (typeof this.props[event[1]] !== 'undefined') {
138-
this.el.on(event[0], this.props[event[1]]);
121+
attachEventHandlers(props) {
122+
props.events.forEach(event => {
123+
if (typeof props[event[1]] !== 'undefined') {
124+
this.el.on(event[0], props[event[1]]);
139125
}
140126
});
141127
}
142128

143-
detachEventHandlers() {
144-
this.props.events.forEach(event => {
145-
if (typeof this.props[event[1]] !== 'undefined') {
129+
detachEventHandlers(props) {
130+
props.events.forEach(event => {
131+
if (typeof props[event[1]] !== 'undefined') {
146132
this.el.off(event[0]);
147133
}
148134
});
149135
}
150136

137+
prepareValue(value, defaultValue) {
138+
const issetValue = typeof value !== 'undefined' && value !== null;
139+
const issetDefaultValue = typeof defaultValue !== 'undefined';
140+
141+
if (!issetValue && issetDefaultValue) {
142+
return defaultValue;
143+
}
144+
return value;
145+
}
146+
147+
prepareOptions(options) {
148+
const opt = options;
149+
if (typeof opt.dropdownParent === 'string') {
150+
opt.dropdownParent = $(opt.dropdownParent);
151+
}
152+
return opt;
153+
}
154+
151155
isObject(value) {
152156
const type = typeof value;
153157
return type === 'function' || (value && type === 'object') || false;

0 commit comments

Comments
 (0)