Stop abusing the optional chaining operator (?.)!

Click on the front-end Q above and follow the public account

Reply to join the group and join the front-end Q technology exchange group

Foreword

I have sorted out some of the more typical code specification issues in recent products. There is a specification about optional chain operator (?.). I just mentioned it at the time. I will talk about it in detail today. Ideas, everyone is welcome to participate in the discussion.

Everyone is familiar with the optional chaining operator (?.). Let’s look at an example:

const result = obj?.a?.b?.c?.d

Very simple example, the above code? If the previous attribute is null (null or undefined), the result value is undefined. Otherwise, if neither is null, the last d attribute value will be returned.

This article does not explain the usage of this grammar. It mainly wants to analyze the abuse and misuse of this grammar in daily development.

Abuse, misuse

Recently, I was code reviewing a company project code and found that the optional chaining operator used in the code was abused a lot and used in a very stupid way. I often encountered this kind of code:

const userName = data?.items?.[0]?.user?.name

↑ No matter whether the object or attribute may have a null value, just add ?. and you’re done.

// react class component
const name = this.state?.name

// react hooks
const [items, setItems] = useState([])
items?.map(...)
setItems?.([]) // It’s really written like this

↑ Under the React framework, the value of this.state cannot be a null value. The initialization and set values are all arrays. There is no need to add ?.

const item1 = obj?.item1
console.log(item1.name)

↑ The first line of code indicates that obj or item1 may be a null value, but the second line also clearly indicates that it cannot be a null value, otherwise an error will still be thrown, and the ?. in the first line is meaningless. .

if (obj?.item1?.item2) {
    const item2 = obj?.item1?.item2
    const name = obj?.item1?.item2?.name
}

↑ It has already been judged that it is not empty in if, so there is no need to judge that it is not empty internally.

Problems and shortcomings

If you don’t consider the necessity of using ?., mindless abuse is actually no problem, it will not affect the function, and there are many advantages:

  1. There is no need to consider whether it is non-empty, just add ?. after each variable or attribute and that’s it.

  2. Because there is no need to think, development efficiency is high.

  3. There will be no null reference errors, no problems with the page becoming unresponsive or playing incorrectly.

But the problems and shortcomings are also obvious and serious. Let’s analyze it in two points:

  1. Readability and maintainability: It brings a lot of interference to code maintainers in analyzing the code, and the readability and maintainability of the code are very poor.

  2. Implicitly filtered exceptions: Exceptions are implicitly filtered, making it impossible to quickly locate the problem.

  3. Compiled code redundancy.

  4. Eye protection: A string of ? looks uncomfortable, especially from the perspective of a code reviewer.

1. Readability and maintainability

Readability and maintainability are actually the same thing. They both refer to the efficiency of problem-solving by developers and maintainers who are not the authors of the source code when figuring out the logic of the code, modifying bugs, etc. The better the code is written, the faster it can be handled. If the writing is bad, the processing will be slow. It’s simple.

const onClick = () => {
    const user = props.data?.items?.[0]?.user
    if (user) {
        // use user to do something
    }
}

Take this line of code as an example. There is a bug that causes no response when clicking a button. When maintenance and development see this code, they will wonder if there may be a null value in this string of chained attributes, which results in user being null. Value, not entering the if, resulting in no response. Then continue to analyze the props transmission code of the upper-layer component to see where the data value comes from, and see if any piece of code causes the data or items to have null values. . .

Actually? There will be no null values in the string of attributes passed from the outside, so the bug problem is not here at all.

const user = props.data.items[0].user

What about removing all ?.? Maintenance and development tracking issues When I see this line of code, the attributes of data items must not be null, otherwise the console will throw an error. However, there is no error in the bug phenomenon, so you only need to check whether user can be null. It is very Many situations can be easily ruled out.

The summary is: it brings a lot of interference to code maintainers in analyzing the code, and the code readability and maintainability are very poor.

2. Implicitly filtered exceptions

api.get(...).then(result => {
    const id = result?.id
    // use id to do something
})

For example, there is a requirement. When obtaining data from the backend API, the id attribute in the result needs to be obtained, and then the data is processed. From a business process perspective, the result and id returned by this API must have values. If not, the subsequent process will It will be impossible.

Then, due to a problem with the writing of the background logic, result=null is returned in some cases. However, because ?. is added to the front end, there is no response on the page, the js does not throw an error, and there is no log in the console. If something goes wrong in the subsequent process, it will be very difficult to find the cause. It’s okay to be familiar with the code. If you didn’t write it yourself, you can only look at the code and figure out the logic. If the production environment is compressed and confused, it will be even harder to troubleshoot.

api.get(...).then(result => {
    const id = result.id
    // use id to do something
})

How about removing ?.? If there is a problem with the api return value, an error will be thrown immediately, and the subsequent process will not proceed. Regardless of the development or production environment, the problem can be quickly located in the console. Even if it is compressed and confused, you can tell something about it from the error. , or it can also be monitored in some front-end monitoring programs.

In fact, this phenomenon is similar to not adding throw in try catch, which completely filters out implicit exception errors, such as the following example:

//This try is intended to handle api request exceptions
try {
    const data = getSaveData() // This js logic is also in try, so if this method throws an error internally, there will be no response on the page, making it difficult to track the problem.
    const result = await api.post(url, data)
    // result logical processing
} catch (e) {
    // It's better to pop up a box and log, or even do nothing at all.
}

The summary is: exceptions are implicitly filtered out, resulting in the inability to quickly locate the problem.

3. Code redundancy after compilation

If the code is ts and the compilation target is ES2016, the compiled code will be very long. You can take a look at the effect of www.typescriptlang.org/play.

a146d98b029f958edfe03bfb9e8c306f.png

Babel has the same compilation effect under individual stages.

7b57df8310fa1503860f7789722a6c39.png

But that doesn’t mean that it’s not used at all, it means that it is abused as much as possible, so that the frequency of use will be much less, and the compiled code surplus will be much less.

How should

be used?

Having said so much, how should .? be used? Does that mean it’s not necessary? Of course it doesn’t mean it cannot be used. This feature definitely has many benefits for development, but it must be used reasonably and cannot be abused.

  • Avoid blind use or abuse, and add question marks whenever there is a point, especially after each attribute in a relatively long chain code.

  • Only use it if it may be a null value and there is a null value in the business logic; try not to use it in other situations.

In fact, to put it bluntly: when does it need to be judged that a variable or attribute is not empty, and when does it not. First of all, when using it, you have to think about whether the variable or attribute value before the question mark may be a null value:

  1. Obviously it cannot be a null value, such as this.state this.props in React class components. Do not use it;

  2. Do not use variables or attributes that you define yourself and are not assigned a null value;

  3. In some methods or components, parameters and properties do not allow null values, so there is no need to judge whether they are non-null in the methods or components. (For common ones, it is recommended to write assertions, or to judge the null value situation and throw error)

  4. In the background API request result, the result or its internal attributes must have values, so these values do not need to be judged to be non-null.

  5. According to the normal process, a certain data will not have a null value. If it is a null value, it means there is a problem with the previous process. In this case, there is no need to judge whether it is null in the logic.

const userName = data?.items?.[0]?.user?.name // Don't abuse it. If an attribute may be null, you need ?.
const userName = data.items[0].user?.name // For example, the data.items array is definitely not an empty array
const items2 = items1.filter(item => item.checked)
if (items2?.length) { } // Not needed?.
// react class component
const name = this.state?.name // No need?.

// react hooks
const [items, setItems] = useState([])
items?.map(...) // If setItems is not assigned a null value, then ?. is not needed.
setItems?.([]) // Not required?.
const item1 = obj?.item1 // No need?.
console.log(item1.name)
const id = obj?.id // The following code has already stated that it cannot be a null value, so there is no need for ?.
const name = obj.name
if (obj?.item1?.item2) {
    const item2 = obj?.item1?.item2 // No need?.
    const name = obj?.item1?.item2?.name // No need?.
}
const id = obj?.item?.id // No need?.
api.get(id).then(...) // If the id of this API is a null value, the API will throw an error

Of course, when writing code, you have to think more about whether the attribute may have a null value, which will affect development efficiency to a certain extent. There are also developers who will find it annoying, do not understand, and write too many ?. without thinking. It’s easy, but let me analyze it from two other perspectives:

  1. I think a qualified developer should be very familiar with the logic of his own code, and should have the responsibility to know which values may be null values and which values cannot be null values (not saying all, there are most of them), otherwise it will be a violation of his own code I know very little about it and just think that the code can run, but the quality of the code will naturally be low.

  2. Think about how everyone wrote before this new feature came out. Each variable and attribute would be added with if non-empty judgment or use logical AND ( & amp; & amp;)? No way.

Summary

This article analyzes the abuse of the optional chaining operator (?.) feature and the “correct usage” from the perspective of a code reviewer. It only represents my own opinion. Everyone is welcome to participate in the discussion. Any rebuttal is unconditionally accepted.

Disadvantages of abuse:

  1. Readability and maintainability: It brings a lot of interference to code maintainers in analyzing the code, and the readability and maintainability of the code are very poor.

  2. Implicitly filtered exceptions: Exceptions are implicitly filtered, making it impossible to quickly locate the problem.

  3. Compiled code redundancy.

  4. Eye protection: A string of ? looks uncomfortable, especially from the perspective of a code reviewer.

“Correct usage”:

  1. Avoid blind use or abuse, and add question marks whenever there is a point, especially after each attribute in a relatively long chain code.

  2. Only use it if it may be a null value and there is a null value in the business logic; try not to use it in other situations.

Author: Mark Big Bear

Link: https://juejin.cn/post/7280747572707999799

d477a2290a2caf0b18ce5e2344b012cf.png

Recommended in the past

As a freelancer, how do I prepare for interviews after a one-year gap period?

6e0e9e99658a1696b3fd1f5d3a2bb0a6.png

Share a performance optimization using task slicing to solve page lag~

1480379ee05271bb51303a401e27d3a5.png

The new front-end feature, Compute Pressure API, can observe CPU changes! ! !

57afeeef37d70e527b6a265e82737416.png

at last

  • Welcome to add me on WeChat and join the technical group for long-term communication and learning…

  • Welcome to pay attention to “Front-End Q”, learn front-end seriously, and become a professional technical person…

25e00fb9d5aedb001ca9baf921fe9e29.jpeg

2a084fd34d13907e2c5f2dbabd392d10.png

Click Watching to support me

4a2ac60ec762a83f03215488b9b49b65.gif