diff --git a/STYLE.md b/STYLE.md index 4017c5bff9fb..ff6e2c0ae530 100644 --- a/STYLE.md +++ b/STYLE.md @@ -175,6 +175,23 @@ Empty functions (noop) should be declare as arrow functions with no whitespace i } ``` +## Accessing Object Properties and Default Values + +Use `_.get()` to safely access object properties and `||` to short circuit null or undefined values that are not guaranteed to exist in a consistent way throughout the codebase. In the rare case that you want to consider a falsy value as usable and the `||` operator prevents this then be explicit about this in your code and check for the type using an underscore method e.g. `_.isBoolean(value)` or `_.isEqual(0)`. + + ```javascript + // Bad + const value = somePossiblyNullThing ?? 'default'; + // Good + const value = somePossiblyNullThing || 'default'; + // Bad + const value = someObject.possiblyUndefinedProperty?.nestedProperty || 'default'; + // Bad + const value = (someObject && someObject.possiblyUndefinedProperty && someObject.possiblyUndefinedProperty.nestedProperty) || 'default'; + // Good + const value = _.get(someObject, 'possiblyUndefinedProperty.nestedProperty', 'default'); + ``` + ## JSDocs - Avoid docs that don't add any additional information. - Always document parameters and return values. @@ -292,7 +309,8 @@ So, if a new language feature isn't something we have agreed to support it's off Here are a couple of things we would ask that you *avoid* to help maintain consistency in our codebase: - **Async/Await** - Use the native `Promise` instead -- **Optional Chaining** - Use `lodash/get` to fetch a nested value instead +- **Optional Chaining** - Use `_.get()` to fetch a nested value instead +- **Null Coalescing Operator** - Use `_.get()` or `||` to set a default value for a possibly `undefined` or `null` variable # React Coding Standards diff --git a/src/components/AddressSearch.js b/src/components/AddressSearch.js index 4f4dc581a886..6de28f19de4a 100644 --- a/src/components/AddressSearch.js +++ b/src/components/AddressSearch.js @@ -36,7 +36,11 @@ const defaultProps = { const AddressSearch = (props) => { const googlePlacesRef = useRef(); useEffect(() => { - googlePlacesRef.current?.setAddressText(props.value); + if (!googlePlacesRef.current) { + return; + } + + googlePlacesRef.current.setAddressText(props.value); }, []); // eslint-disable-next-line diff --git a/src/libs/Network.js b/src/libs/Network.js index a781595089c6..01592b51f381 100644 --- a/src/libs/Network.js +++ b/src/libs/Network.js @@ -189,7 +189,7 @@ function processNetworkRequestQueue() { } const requestData = queuedRequest.data; - const requestEmail = requestData.email ?? ''; + const requestEmail = lodashGet(requestData, 'email', ''); // If we haven't passed an email in the request data, set it to the current user's email if (email && _.isEmpty(requestEmail)) { diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b64e348c7122..29e8b68227c6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -85,7 +85,8 @@ function createIOUTransaction(params) { .then((data) => { getIOUReportsForNewTransaction([data]); Navigation.navigate(ROUTES.getReportRoute(data.chatReportID)); - })?.catch((error) => { + }) + .catch((error) => { Onyx.merge(ONYXKEYS.IOU, { loading: false, creatingIOUTransaction: false, @@ -134,7 +135,8 @@ function createIOUSplit(params) { } getIOUReportsForNewTransaction(reportParams); Navigation.navigate(ROUTES.getReportRoute(chatReportID)); - })?.catch((error) => { + }) + .catch((error) => { Onyx.merge(ONYXKEYS.IOU, { loading: false, creatingIOUTransaction: false, diff --git a/src/libs/fileDownload/index.js b/src/libs/fileDownload/index.js index aaddeee433ed..dea9353140a2 100644 --- a/src/libs/fileDownload/index.js +++ b/src/libs/fileDownload/index.js @@ -23,7 +23,7 @@ export default function fileDownload(url, fileName) { link.style.display = 'none'; link.setAttribute( 'download', - fileName ?? getAttachmentName(url), // generating the file name + fileName || getAttachmentName(url), // generating the file name ); // Append to html link element page diff --git a/src/libs/fileDownload/index.native.js b/src/libs/fileDownload/index.native.js index 064738142414..60c6dfd66234 100644 --- a/src/libs/fileDownload/index.native.js +++ b/src/libs/fileDownload/index.native.js @@ -60,7 +60,7 @@ function handleDownload(url, fileName) { // android files will download to Download directory // ios files will download to documents directory const path = getPlatform() === 'android' ? dirs.DownloadDir : dirs.DocumentDir; - const attachmentName = fileName ?? getAttachmentName(url); + const attachmentName = fileName || getAttachmentName(url); // fetching the attachment const fetchedAttachment = RNFetchBlob.config({ @@ -75,18 +75,20 @@ function handleDownload(url, fileName) { // resolving the fetched attachment fetchedAttachment.then((attachment) => { - if (attachment?.info()) { - showAlert({ - title: 'Downloaded!', - message: 'Attachment successfully downloaded', - options: [ - { - text: 'OK', - style: 'cancel', - }, - ], - }); + if (!attachment || !attachment.info()) { + return; } + + showAlert({ + title: 'Downloaded!', + message: 'Attachment successfully downloaded', + options: [ + { + text: 'OK', + style: 'cancel', + }, + ], + }); }).catch(() => { showAlert({ title: 'Attachment Error', diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index d06a86612541..0061cda49140 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; @@ -263,7 +264,7 @@ class NewChatPage extends Component { forceTextUnreadStyle /> {!this.props.isGroupChat && } - {this.props.isGroupChat && this.state.selectedOptions?.length > 0 && ( + {this.props.isGroupChat && lodashGet(this.state, 'selectedOptions', []).length > 0 && (