Skip to content

Commit b88455e

Browse files
authored
Merge pull request #6234 from Expensify/Rory-NoPropsDestructuring
[No QA] No Props/State Destructuring
2 parents 180c7e7 + be757dc commit b88455e

File tree

90 files changed

+770
-1000
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+770
-1000
lines changed

STYLE.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,7 @@ const {name, accountID, email} = data;
264264

265265
**React Components**
266266

267-
- Avoid destructuring props and state at the *same time*. It makes the source of a given variable unclear.
268-
- Avoid destructuring either props or state when there are other variables declared in a render block. This helps us quickly know which variables are from props, state, or declared inside the render.
269-
- Use parameter destructuring for stateless function components when there are no additional variable declarations in the render.
267+
Don't destructure props or state. It makes the source of a given variable unclear. This guideline helps us quickly know which variables are from props, state, or from some other scope.
270268
271269
```javascript
272270
// Bad
@@ -276,12 +274,20 @@ render() {
276274
...
277275
}
278276
279-
// Good
277+
// Bad
280278
const UserInfo = ({name, email}) => (
281-
<div>
282-
<p>Name: {name}</p>
283-
<p>Email: {email}</p>
284-
</div>
279+
<View>
280+
<Text>Name: {name}</Text>
281+
<Text>Email: {email}</Text>
282+
</View>
283+
);
284+
285+
// Good
286+
const UserInfo = props => (
287+
<View>
288+
<Text>Name: {props.name}</Text>
289+
<Text>Email: {props.email}</Text>
290+
</View>
285291
);
286292
```
287293

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@
146146
"electron-notarize": "^1.0.0",
147147
"electron-reloader": "^1.2.0",
148148
"eslint": "^7.6.0",
149-
"eslint-config-expensify": "2.0.17",
149+
"eslint-config-expensify": "^2.0.18",
150150
"eslint-loader": "^4.0.2",
151151
"eslint-plugin-detox": "^1.0.0",
152152
"eslint-plugin-jest": "^24.1.0",

src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _ from 'underscore';
12
import React from 'react';
23
import {Pressable, StyleSheet} from 'react-native';
34
import lodashGet from 'lodash/get';
@@ -9,31 +10,22 @@ import {CONTEXT_MENU_TYPES} from '../../../pages/home/report/ContextMenu/Context
910
import AttachmentView from '../../AttachmentView';
1011
import fileDownload from '../../../libs/fileDownload';
1112

12-
1313
/*
1414
* This is a default anchor component for regular links.
1515
*/
16-
const BaseAnchorForCommentsOnly = ({
17-
href,
18-
rel,
19-
target,
20-
children,
21-
style,
22-
fileName,
23-
...props
24-
}) => {
16+
const BaseAnchorForCommentsOnly = (props) => {
2517
let linkRef;
18+
const rest = _.omit(props, _.keys(propTypes));
2619
return (
27-
2820
props.isAttachment
2921
? (
3022
<Pressable onPress={() => {
31-
fileDownload(href, fileName);
23+
fileDownload(props.href, props.fileName);
3224
}}
3325
>
3426
<AttachmentView
35-
sourceURL={href}
36-
file={{name: fileName}}
27+
sourceURL={props.href}
28+
file={{name: props.fileName}}
3729
shouldShowDownloadIcon
3830
/>
3931
</Pressable>
@@ -45,22 +37,25 @@ const BaseAnchorForCommentsOnly = ({
4537
showContextMenu(
4638
CONTEXT_MENU_TYPES.LINK,
4739
event,
48-
href,
40+
props.href,
4941
lodashGet(linkRef, 'current'),
5042
);
5143
}
5244
}
5345
>
5446
<Text
5547
ref={el => linkRef = el}
56-
style={StyleSheet.flatten(style)}
48+
style={StyleSheet.flatten(props.style)}
5749
accessibilityRole="link"
58-
href={href}
59-
hrefAttrs={{rel, target}}
60-
// eslint-disable-next-line react/jsx-props-no-spreading
61-
{...props}
50+
href={props.href}
51+
hrefAttrs={{
52+
rel: props.rel,
53+
target: props.target,
54+
}}
55+
// eslint-disable-next-line react/jsx-props-no-spreading
56+
{...rest}
6257
>
63-
{children}
58+
{props.children}
6459
</Text>
6560
</PressableWithSecondaryInteraction>
6661
)

src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _ from 'underscore';
12
import React from 'react';
23
import lodashGet from 'lodash/get';
34
import {Linking, StyleSheet, Pressable} from 'react-native';
@@ -13,27 +14,21 @@ import styles from '../../../styles/styles';
1314
/*
1415
* This is a default anchor component for regular links.
1516
*/
16-
const BaseAnchorForCommentsOnly = ({
17-
href,
18-
children,
19-
style,
20-
isAttachment,
21-
fileName,
22-
...props
23-
}) => {
17+
const BaseAnchorForCommentsOnly = (props) => {
2418
let linkRef;
19+
const rest = _.omit(props, _.keys(propTypes));
2520
return (
26-
isAttachment
21+
props.isAttachment
2722
? (
2823
<Pressable
2924
style={styles.mw100}
3025
onPress={() => {
31-
fileDownload(href, fileName);
26+
fileDownload(props.href, props.fileName);
3227
}}
3328
>
3429
<AttachmentView
35-
sourceURL={href}
36-
file={{name: fileName}}
30+
sourceURL={props.href}
31+
file={{name: props.fileName}}
3732
shouldShowDownloadIcon
3833
/>
3934
</Pressable>
@@ -45,20 +40,20 @@ const BaseAnchorForCommentsOnly = ({
4540
showContextMenu(
4641
CONTEXT_MENU_TYPES.LINK,
4742
event,
48-
href,
43+
props.href,
4944
lodashGet(linkRef, 'current'),
5045
);
5146
}
5247
}
53-
onPress={() => Linking.openURL(href)}
48+
onPress={() => Linking.openURL(props.href)}
5449
>
5550
<Text
5651
ref={el => linkRef = el}
57-
style={StyleSheet.flatten(style)}
58-
// eslint-disable-next-line react/jsx-props-no-spreading
59-
{...props}
52+
style={StyleSheet.flatten(props.style)}
53+
// eslint-disable-next-line react/jsx-props-no-spreading
54+
{...rest}
6055
>
61-
{children}
56+
{props.children}
6257
</Text>
6358
</PressableWithSecondaryInteraction>
6459
)

src/components/AvatarWithImagePicker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class AvatarWithImagePicker extends React.Component {
157157
}
158158

159159
render() {
160-
const {DefaultAvatar} = this.props;
160+
const DefaultAvatar = this.props.DefaultAvatar;
161161
const additionalStyles = _.isArray(this.props.style) ? this.props.style : [this.props.style];
162162

163163
const indicatorStyles = [

src/components/BigNumberPad.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const padNumbers = [
1717
['.', '0', '<'],
1818
];
1919

20-
const BigNumberPad = ({numberPressed}) => (
20+
const BigNumberPad = props => (
2121
<View style={[styles.flexColumn, styles.w100]}>
2222
{_.map(padNumbers, (row, rowIndex) => (
2323
<View key={`NumberPadRow-${rowIndex}`} style={[styles.flexRow, styles.mt3]}>
@@ -30,7 +30,7 @@ const BigNumberPad = ({numberPressed}) => (
3030
key={column}
3131
style={[styles.flex1, marginLeft]}
3232
text={column}
33-
onPress={() => numberPressed(column)}
33+
onPress={() => props.numberPressed(column)}
3434
/>
3535
);
3636
})}

src/components/Button.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Button extends Component {
109109
}
110110

111111
renderContent() {
112-
const {ContentComponent} = this.props;
112+
const ContentComponent = this.props.ContentComponent;
113113
if (ContentComponent) {
114114
return <ContentComponent />;
115115
}

src/components/Checkbox.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,17 @@ const defaultProps = {
2424
disabled: false,
2525
};
2626

27-
const Checkbox = ({
28-
isChecked,
29-
onPress,
30-
hasError,
31-
disabled,
32-
}) => (
27+
const Checkbox = props => (
3328
<Pressable
34-
disabled={disabled}
35-
onPress={() => onPress(!isChecked)}
29+
disabled={props.disabled}
30+
onPress={() => props.onPress(!props.isChecked)}
3631
>
3732
<View
3833
style={[
3934
styles.checkboxContainer,
40-
isChecked && styles.checkedContainer,
41-
hasError && styles.borderColorDanger,
42-
disabled && styles.cursorDisabled,
35+
props.isChecked && styles.checkedContainer,
36+
props.hasError && styles.borderColorDanger,
37+
props.disabled && styles.cursorDisabled,
4338
]}
4439
>
4540
<Icon src={Checkmark} fill="white" height={14} width={14} />

src/components/CheckboxWithLabel.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,25 @@ const defaultProps = {
3838
errorText: '',
3939
};
4040

41-
const CheckboxWithLabel = ({
42-
LabelComponent, isChecked, onPress, style, label, hasError, errorText,
43-
}) => {
41+
const CheckboxWithLabel = (props) => {
42+
const LabelComponent = props.LabelComponent;
4443
const defaultStyles = [styles.flexRow, styles.alignItemsCenter];
45-
const wrapperStyles = _.isArray(style) ? [...defaultStyles, ...style] : [...defaultStyles, style];
44+
const wrapperStyles = _.isArray(props.style) ? [...defaultStyles, ...props.style] : [...defaultStyles, props.style];
4645

47-
if (!label && !LabelComponent) {
46+
if (!props.label && !LabelComponent) {
4847
throw new Error('Must provide at least label or LabelComponent prop');
4948
}
5049
return (
5150
<>
5251
<View style={wrapperStyles}>
5352
<Checkbox
54-
isChecked={isChecked}
55-
onPress={() => onPress(!isChecked)}
56-
label={label}
57-
hasError={hasError}
53+
isChecked={props.isChecked}
54+
onPress={() => props.onPress(!props.isChecked)}
55+
label={props.label}
56+
hasError={props.hasError}
5857
/>
5958
<TouchableOpacity
60-
onPress={() => onPress(!isChecked)}
59+
onPress={() => props.onPress(!props.isChecked)}
6160
style={[
6261
styles.ml3,
6362
styles.pr2,
@@ -68,17 +67,17 @@ const CheckboxWithLabel = ({
6867
styles.alignItemsCenter,
6968
]}
7069
>
71-
{label && (
70+
{props.label && (
7271
<Text style={[styles.ml2]}>
73-
{label}
72+
{props.label}
7473
</Text>
7574
)}
7675
{LabelComponent && (<LabelComponent />)}
7776
</TouchableOpacity>
7877
</View>
79-
{!_.isEmpty(errorText) && (
78+
{!_.isEmpty(props.errorText) && (
8079
<InlineErrorText>
81-
{errorText}
80+
{props.errorText}
8281
</InlineErrorText>
8382
)}
8483
</>

src/components/DatePicker/index.android.js

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,36 +38,25 @@ class DatePicker extends React.Component {
3838
}
3939

4040
render() {
41-
const {
42-
value,
43-
label,
44-
placeholder,
45-
hasError,
46-
errorText,
47-
translateX,
48-
containerStyles,
49-
disabled,
50-
} = this.props;
51-
52-
const dateAsText = value ? moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING) : '';
41+
const dateAsText = this.props.value ? moment(this.props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) : '';
5342

5443
return (
5544
<>
5645
<ExpensiTextInput
57-
label={label}
46+
label={this.props.label}
5847
value={dateAsText}
59-
placeholder={placeholder}
60-
hasError={hasError}
61-
errorText={errorText}
62-
containerStyles={containerStyles}
63-
translateX={translateX}
48+
placeholder={this.props.placeholder}
49+
hasError={this.props.hasError}
50+
errorText={this.props.errorText}
51+
containerStyles={this.props.containerStyles}
52+
translateX={this.props.translateX}
6453
onPress={this.showPicker}
6554
editable={false}
66-
disabled={disabled}
55+
disabled={this.props.disabled}
6756
/>
6857
{this.state.isPickerVisible && (
6958
<RNDatePicker
70-
value={value ? moment(value).toDate() : new Date()}
59+
value={this.props.value ? moment(this.props.value).toDate() : new Date()}
7160
mode="date"
7261
onChange={this.raiseDateChange}
7362
/>

0 commit comments

Comments
 (0)