Skip to content

Commit 8f4b172

Browse files
authored
Picker - Remove UNSAFE method (#922)
* Replace UNSAFE method; extend PureComponent; Wrap with `asBaseComponent` HOC; fix test's import * Addressing state (inner) updates to 'value' prop * fix for 'custom modal' example
1 parent 323c02b commit 8f4b172

File tree

3 files changed

+44
-31
lines changed

3 files changed

+44
-31
lines changed

demo/src/screens/componentScreens/PickerScreen.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,16 @@ export default class PickerScreen extends Component {
5858
};
5959

6060
renderDialog = modalProps => {
61-
const {visible, toggleModal, children} = modalProps;
62-
61+
const {visible, children, toggleModal, onDone} = modalProps;
62+
6363
return (
6464
<Dialog
6565
migrate
6666
visible={visible}
67-
onDismiss={() => toggleModal(false)}
67+
onDismiss={() => {
68+
onDone();
69+
toggleModal(false);
70+
}}
6871
width="100%"
6972
height="45%"
7073
bottom

src/components/picker/__tests__/index.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Picker from '../index';
1+
import {Picker} from '../index';
22

33
const countries = [
44
{label: 'Israel', value: 'IL'},

src/components/picker/index.js

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
import _ from 'lodash';
55
import PropTypes from 'prop-types';
6-
import React from 'react';
7-
import {BaseComponent} from '../../commons';
6+
import React, {PureComponent} from 'react';
7+
import {asBaseComponent, forwardRef} from '../../commons';
88
import View from '../../components/view';
99
import Modal from '../modal';
1010
import Button from '../../components/button';
@@ -14,6 +14,7 @@ import NativePicker from './NativePicker';
1414
import PickerModal from './PickerModal';
1515
import PickerItem from './PickerItem';
1616

17+
1718
const PICKER_MODES = {
1819
SINGLE: 'SINGLE',
1920
MULTI: 'MULTI'
@@ -28,7 +29,7 @@ const ItemType = PropTypes.shape({
2829
* @gif: https://media.giphy.com/media/3o751SiuZZiByET2lq/giphy.gif, https://media.giphy.com/media/TgMQnyw5grJIDohzvx/giphy.gif, https://media.giphy.com/media/5hsdmVptBRskZKn787/giphy.gif
2930
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/PickerScreen.js
3031
*/
31-
class Picker extends BaseComponent {
32+
class Picker extends PureComponent {
3233
static displayName = 'Picker';
3334
static propTypes = {
3435
...TextField.propTypes,
@@ -142,6 +143,7 @@ class Picker extends BaseComponent {
142143

143144
this.state = {
144145
value: props.value,
146+
prevValue: undefined,
145147
selectedItemPosition: 0,
146148
items: this.extractPickerItems(props)
147149
};
@@ -160,20 +162,30 @@ class Picker extends BaseComponent {
160162
// }
161163
}
162164

163-
UNSAFE_componentWillReceiveProps(nexProps) {
164-
this.setState({
165-
value: nexProps.value
166-
});
165+
static getDerivedStateFromProps(nextProps, prevState) {
166+
if (!_.isEmpty(nextProps.value) && prevState.value !== nextProps.value) {
167+
if (prevState.prevValue !== prevState.value) { // for this.setState() updates to 'value'
168+
// NOTE: this.setState() already updated the 'value' so here we only updating the 'prevValue'
169+
return {
170+
prevValue: prevState.value
171+
};
172+
} else { // for prop update to 'value'
173+
return {
174+
value: nextProps.value
175+
};
176+
}
177+
}
178+
return null;
167179
}
168180

169181
getAccessibilityInfo() {
170182
const {placeholder} = this.props;
183+
171184
return {
172185
accessibilityLabel: this.getLabelValueText() ? `${placeholder}. selected. ${this.getLabelValueText()}` : `Select ${placeholder}`,
173186
accessibilityHint: this.getLabelValueText()
174187
? 'Double tap to edit'
175-
: `Goes to ${placeholder}. Suggestions will be provided`,
176-
...this.extractAccessibilityProps()
188+
: `Goes to ${placeholder}. Suggestions will be provided`
177189
};
178190
}
179191

@@ -191,6 +203,7 @@ class Picker extends BaseComponent {
191203
getLabelValueText = () => {
192204
const {value: propsValue} = this.props;
193205
const {value: stateValue} = this.props;
206+
194207
if (this.shouldNotChangePickerLabelWhileSelecting()) {
195208
return this.getLabel(propsValue);
196209
}
@@ -223,6 +236,7 @@ class Picker extends BaseComponent {
223236
// otherwise, extract from picker items
224237
const {items} = this.state;
225238
const selectedItem = _.find(items, {value});
239+
226240
return _.get(selectedItem, 'label');
227241
}
228242

@@ -240,39 +254,29 @@ class Picker extends BaseComponent {
240254
const {getItemValue} = this.props;
241255
const {value} = this.state;
242256
const newValue = _.xorBy(value, [item], getItemValue || 'value');
243-
this.setState({
244-
value: newValue
245-
});
257+
258+
this.setState({value: newValue});
246259
};
247260

248261
cancelSelect = () => {
249-
this.setState({
250-
value: this.props.value
251-
});
262+
this.setState({value: this.props.value});
252263
this.toggleExpandableModal(false);
253264
_.invoke(this.props, 'topBarProps.onCancel');
254265
};
255266

256267
onDoneSelecting = item => {
257268
this.clearSearchField();
258-
259269
this.setState({value: item});
260270
this.toggleExpandableModal(false);
261271
_.invoke(this.props, 'onChange', item);
262272
};
263273

264274
onSearchChange = searchValue => {
265-
this.setState({
266-
searchValue
267-
});
275+
this.setState({searchValue});
268276
_.invoke(this.props, 'onSearchChange', searchValue);
269277
};
270278

271-
onSelectedItemLayout = ({
272-
nativeEvent: {
273-
layout: {y}
274-
}
275-
}) => {
279+
onSelectedItemLayout = ({nativeEvent: {layout: {y}}}) => {
276280
this.setState({selectedItemPosition: y});
277281
};
278282

@@ -290,6 +294,7 @@ class Picker extends BaseComponent {
290294
if (!showSearch || _.isEmpty(searchValue) || _.includes(_.lowerCase(childLabel), _.lowerCase(searchValue))) {
291295
const selectedValue = PickerPresenter.getItemValue({value, getItemValue});
292296
const isSelected = PickerPresenter.isItemSelected(childValue, selectedValue);
297+
293298
return React.cloneElement(child, {
294299
isSelected,
295300
onPress: mode === Picker.modes.MULTI ? this.toggleItemSelection : this.onDoneSelecting,
@@ -318,7 +323,7 @@ class Picker extends BaseComponent {
318323
renderCustomModal,
319324
listProps,
320325
testID
321-
} = this.getThemeProps();
326+
} = this.props;
322327
const {showExpandableModal, selectedItemPosition, value} = this.state;
323328
const children = this.appendPropsToChildren(this.props.children);
324329

@@ -331,6 +336,7 @@ class Picker extends BaseComponent {
331336
onDone: () => this.onDoneSelecting(value),
332337
onCancel: this.cancelSelect
333338
};
339+
334340
return renderCustomModal(modalProps);
335341
}
336342

@@ -366,6 +372,7 @@ class Picker extends BaseComponent {
366372

367373
if (_.isFunction(renderPicker)) {
368374
const {value} = this.state;
375+
369376
return (
370377
<View left>
371378
<Button {...customPickerProps} link onPress={this.handlePickerOnPress} testID={testID}>
@@ -376,8 +383,9 @@ class Picker extends BaseComponent {
376383
);
377384
}
378385

379-
const textInputProps = TextField.extractOwnProps(this.getThemeProps());
386+
const textInputProps = TextField.extractOwnProps(this.props);
380387
const label = this.getLabelValueText();
388+
381389
return (
382390
<TextField
383391
{...textInputProps}
@@ -393,4 +401,6 @@ class Picker extends BaseComponent {
393401
}
394402

395403
Picker.Item = PickerItem;
396-
export default Picker;
404+
405+
export {Picker}; // For tests
406+
export default asBaseComponent(forwardRef(Picker));

0 commit comments

Comments
 (0)