在JSX中使用三元组而不是&&。

73 阅读3分钟

这个代码有什么问题?

function ContactList({contacts}) {
  return (
    
      
        {contacts.length &&
          contacts.map(contact => (
            
              {contact.firstName} {contact.lastName}
            
          ))}
      
    
  )
}

不确定吗?让我再问你一个问题。如果contacts[] ,上述代码会发生什么?没错!你会呈现出!你会呈现0!

我曾经把这个错误运到PayPal的生产部门。不是开玩笑。它是在这个页面上。

PayPal page with contacts

当一个没有联系人的用户来时,他们看到的就是这个。

PayPal page with 0 instead of contacts

是的,这很不好玩...为什么会这样呢?因为当JavaScript评估0 && anything 时,结果总是 0 ,因为0 是虚假的,所以它不会评估&& 的右边。

解决办法是什么?使用三元组来明确说明你希望在falsy情况下呈现的内容。在我们的例子中,它什么都不是(所以使用null 是完美的)。

function ContactList({contacts}) {
  return (
    
      
        {contacts.length
          ? contacts.map(contact => (
              
                {contact.firstName} {contact.lastName}
              
            ))
          : null}
      
    
  )
}

那么这段代码呢?这里有什么问题吗?

function Error({error}) {
  return error && {error.message}
}

不确定吗?如果errorundefined 呢?如果是这样的话,你会得到以下错误。

Uncaught Error: Error(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

或者,在生产中,你可能会得到这个。

react-dom.production.min.js:13 Uncaught Invariant Violation: Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=f for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

这里的问题是,undefined && anything 将总是评估为undefined

实际上,这两种情况下的问题是我们在使用&& 来做条件渲染。换句话说,我们在用&& 来做条件性的参数传递。记住,JSX是对调用React.createElement 的简单语法抽象。所以这就像试图写这个。

function throwTheCandy(candyNames) {
  for (const candyName of candyNames) {
    throwCandy(candyName)
  }
}

throwTheCandy(candies.length && candies.map(c => c.name))

那就没有任何意义了,对吗?我们的类型都被搞乱了。React之所以允许你这样做,是因为渲染0 是有效的。

幸运的是,TypeScript可以帮助你避免第二个例子,即意外地返回undefined 。但在这层保护之上,我们如何更明确地说明我们的意图。如果你想做一些有条件的事情,不要滥用逻辑和运算符(&&)。

实际上,这似乎是反直觉的,因为我们经常在一个if 条件中使用&& ,说:如果这两个值都是真实的,那么就做这件事,否则就不做。不幸的是,对于我们的用例来说,&& 操作符也有这样的 "特点",如果两个值都不是真实的,它就会返回错误的那个值。

0 && true // 0
true && 0 // 0
false && true // false
true && '' // ''

所以我强烈建议你不要在你的JSX中滥用&& (或|| ),而是使用实际的分支语法特征,比如三元组(condition ? trueConsequent : falseConsequent ),甚至是if 语句(将来甚至可能做表达式)。

我个人很喜欢三元组,但如果你喜欢if 语句,那也很好。下面是我如何用if 语句做联系人的事情。

function ContactList({contacts}) {
  let contactsElements = null
  if (contacts.length) {
    contactsElements = contacts.map(contact => (
      
        {contact.firstName} {contact.lastName}
      
    ))
  }

  return (
    
      {contactsElements}
    
  )
}

就我个人而言,我认为三元语句更具可读性(如果你不同意,请记住,"可读性 "是主观的,某物的可读性与熟悉程度有很大关系,而不是其他什么)。无论你做什么,你做你的,只是不要做bug。

使用实际分支语法的另一个好处是,测试覆盖率工具可以检测并指出你的测试何时没有覆盖其中一个分支。

我很清楚,我们可以通过使用!!contacts.length && ...contacts.length > 0 && ... 甚至Boolean(contacts.length) && ... 来解决接触问题,但我仍然倾向于不滥用逻辑 AND 操作符来渲染。我更喜欢通过使用三元组来明确。

最后,如果我今天不得不写这两个组件,我会这样写的。

function ContactList({contacts}) {
  return (
    
      
        {contacts.length
          ? contacts.map(contact => (
              
                {contact.firstName} {contact.lastName}
              
            ))
          : null}
      
    
  )
}
function Error({error}) {
  return error ? {error.message} : null
}

祝您好运!