Optimizing non-null judgment chains in JS/React/Vue projects using null-safe syntax

problem background

JavaScript is a highly flexible language, especially the asynchronous programming model of its Promise, which is one of the two asynchronous programming methods that other languages ​​are competing to learn (the other is the coroutine of Go language). Aside from the advantages and disadvantages of weak typing, the non-null check is undoubtedly one of the few ugly codes in javascript:

//最简的写法,如果调用链中有一环为空,会departmentName的类型会是什么?
const departmentName = staff&&staff.department&&staff.department.name;
// 比较靠谱点的写法
const departmentName = staff?staff.department?staff.department.name:null:null;

solution

As such a popular language, how could there not be such excellent null-safe syntax as Swift and Kotlin?

In fact, js's null-safe syntax, optional chained call "?." and null value combination "??" , has been accepted as an ECMAScript standard proposal into the ecma262 standard:

// 可选链式调用?. +++++++++++++++++++++++++++++++++++++++++++++++++
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// Alice只有猫而并没有狗,因此期望输出: undefined

console.log(adventurer.someNonExistentMethod?.());
// 方法someNonExistentMethod并不存在,所以期望输出 output: undefined
// ---------------------------------------------------------------

// 空值合并??,相当于a!=null?a:b的缩写++++++++++++++++++++++++++++++
const foo = null ?? "默认字符串";
console.log(foo);
// 期望输出: "默认字符串"

const baz = 0 ?? 42;
console.log(baz);
// 期望输出: 0
// 注意这里的行为与 a?a:b有一点差异,即0为非空值,更近似a!=null?a:b
// --------------------------------------------------------------

// 组合可选链式调用与空值合并++++++++++++++++++++++++++++++++++++++
const customer = {
  name: "Li Lei",
  details: {
    age: 82,
    location: "Beijin"
	// details 的 address 属性并未定义
  }
};
const customerCity = customer.details?.address?.city ?? "Shanghai";
console.log(customerCity);
// 期望输出:"Shanghai"

// 和函数调用一起使用
function defaultFromCity(){
  console.log("我被逼供了!");
  return "打死你我也不说!";
};
let duration = customer.details?.whereAreYouFrom?.()??defaultFromCity();
// 期望输出:"我被逼供了!"
//          "打死你我也不说!"
customer.details.whereAreYouFrom=()=>"北京";
console.log(customer.details?.whereAreYouFrom?.()??defaultFromCity());
// 期望输出:"北京"
// defaultFromCity并不会被调用,因此未输出"我被逼供了!"
// ---------------------------------------------------------------

Although still in the proposal stage, "?." and "??" have been supported by all PC browsers except IE and most mobile browsers (including Android and iOS default browsers, WebView).

Use in React project

Considering browser compatibility, it is best to compile the code to an earlier version of js code. Fortunately, Babel or Typescript, the two best js compilation frameworks, already support "?." and "??". If we want to use it in React or Vue projects, we only need to introduce Babel's plugin.

  1. Install babel plugin dependencies:
# 安装支持"?."语法的插件
yarn add -D @babel/plugin-proposal-optional-chaining
# 安装支持"??"语法的插件
yarn add -D @babel/plugin-proposal-nullish-coalescing-operator
  1. Configure the babel plugin
// 在工程根目录下的babel.config.js文件中引入插件
module.exports = {
  "plugins": [
    "@babel/plugin-proposal-nullish-coalescing-operator", //支持??
    "@babel/plugin-proposal-optional-chaining",           //支持?.
  ],
};
// 注意没有babel.config.js则创建此文件,有则添加"plugins":[]中的内容

If it is a Vue project or a React project, and "scripts" in package.json is to run start, build and other commands through react-scripts, then the configuration has been completed, and we can start to use "?. " or "??" operator:

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
    const a = {};
    //试试注释下面这行看看效果
    a["b"] = {c: "Hello!"};
    return (
      <div className="App">
        <button type="primary">{a["b"]?.c ?? "Ops!"}</button>;
      </div>;
    );
}

If "scripts" in package.json uses craco to run commands such as start and build (recommended when Ant Design refers to less), the configuration process is exactly the same as above.

If react-app-rewired is used

If "scripts" in package.json uses react-app-rewired to run start, builder and other commands, you need to add the override of useBabelRc() in the configuration file config-overrides of react-app-rewired:

// config-overrides.js
const { override, useBabelRc } = require('customize-cra');
// 配合 react-app-rewired 插件,用来覆盖内置的 webpack 配置
module.exports = override(
  // 原来的其它配置
  // ...
  // 新增:支持babel配置文件+++++++++
  useBabelRc()
  // ------------------------------
);

Of course, you can also directly use the addBabelPlugin function of react-app-rewired to replace the babel configuration file ("2. Configure the babel plugin" with the following configuration):

// config-overrides.js
const { override, fixBabelImports, addBabelPlugin } = require('customize-cra');

// 配合 react-app-rewired 插件,用来覆盖内置的 webpack 配置
module.exports = override(
  // 动态引入 antd 的 css 样式文件
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
  // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addBabelPlugin("@babel/plugin-proposal-optional-chaining"), //支持?.
  addBabelPlugin("@babel/plugin-proposal-nullish-coalescing-operator") //支持??
  // -------------------------------------------------------------------
);

refer to

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324142545&siteId=291194637