React开发需要熟悉的JavaScript特性

Posted 前端之巅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React开发需要熟悉的JavaScript特性相关的知识,希望对你有一定的参考价值。

作者 | Kent C. Dodds
译者 | 张健欣
编辑 | 张之栋、Yonie
React 与其它框架相对,与 javascript 的关系更密切,因此学习 JavaScript 特性可以更加高效地使用 JavaScript 构建应用程序。本文将主要介绍作者最喜欢且最常用的一些 JavaScript 特性。

与我使用过的其它框架相比,我最喜欢 React 的一点是,当你使用它时与 JavaScript 的密不可分。不存在模版 DSL(JSX 直接编译成 JavaScript),组件 API 在添加了 React Hooks 后变得更加简单,而且这个框架在它想要解决的核心 UI 关注点之外提供的抽象概念很少。

因此,学习 JavaScript 特性对于您更高效地使用 React 构建应用程序绝对是明智的。下面是一些我建议您花一些时间学习的 JavaScript 特性,从而让你尽可能高效地使用 React 工作。

React Hooks: https://reactjs.org/hooks  

模板文本
模板文本类似于具有超能力的常规字符串:
const greeting = 'Hello'
const subject = 'World'
console.log(`${greeting} ${subject}!`) // Hello World!
// this is the same as:
console.log(greeting + ' ' + subject + '!')
// in React:
function Box({className, ...props}) {
  return <div className={`box ${className}`} {...props} />
}
速记的属性名
这是如此常见和有用,以至于我现在几乎不假思索就会这样做。
const a = 'hello'
const b = 42
const c = {d: [true, false]}
console.log({a, b, c})
// this is the same as:
console.log({a: a, b: b, c: c})
// in React:
function Counter({initialCount, step}) {
  const [count, setCount] = useCounter({initialCount, step})
  return <button onClick={setCount}>{count}</button>
}
箭头函数
箭头函数是在 JavaScript 中编写函数的另外一种方式,但是他们确实有一些语法区别。幸运的是,在 React 环境下,如果我们在项目中使用钩子(而不是类)的话,我们不需要太关心这个区别。箭头函数允许简短的匿名函数和隐式返回,因此你将看到并希望大量使用箭头函数。
const getFive = () => 5
const addFive = a => a + 5
const divide = (a, b) => a / b
// this is the same as:
function getFive() {
  return 5
}
function addFive(a) {
  return a + 5
}
function divide(a, b) {
  return a / b
}
// in React:
function TeddyBearList({teddyBears}) {
  return (
    <ul>
      {teddyBears.map(teddyBear => (
        <li key={teddyBear.id}>
          <span>{teddyBear.name}</span>
        </li>
      ))}
    </ul>

  )
}

上面的例子中值得一提的是,圆括号 ( 的闭合。这是使用 JSX 时利用箭头函数的隐式返回能力的一种常见方法。

    解构    
解构可能是我最喜欢的 JavaScript 特性之一。我经常解构对象和数组(而且如果你使用 useState,你可能也会这样喜欢这个特性)。我喜欢它强大的表达能力。
// const obj = {x: 3.6, y: 7.8}
// makeCalculation(obj)
function makeCalculation({x, y: d, z = 4}) {
  return Math.floor((x + d + z) / 3)
}
// this is the same as
function makeCalculation(obj) {
  const {x, y: d, z = 4} = obj
  return Math.floor((x + d + z) / 3)
}
// which is the same as
function makeCalculation(obj) {
  const x = obj.x
  const d = obj.y
  const z = obj.z === undefined ? 4 : obj.z
  return Math.floor((x + d + z) / 3)
}
// in React:
function UserGitHubImg({username = 'ghost', ...props}) {
  return <img src={`https://github.com/${username}.png`} {...props} />
}

MDN: Destructuring assignment https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment 

一定要读这篇 MDN 文章。你一定会学到一些新东西。当你读完了,尝试用解构方式的一行代码来重构如下代码:


function nestedArrayAndObject() {
  // refactor this to a single line of destructuring...
  const info = {
    title: 'Once Upon a Time',
    protagonist: {
      name: 'Emma Swan',
      enemies: [
        {name: 'Regina Mills', title: 'Evil Queen'},
        {name: 'Cora Mills', title: 'Queen of Hearts'},
        {name: 'Peter Pan', title: `The boy who wouldn't grow up`},
        {name: 'Zelena', title: 'The Wicked Witch'},
      ],
    },
  }
  // const {} = info // <-- replace the next few `const` lines with this
  const title = info.title
  const protagonistName = info.protagonist.name
  const enemy = info.protagonist.enemies[3]
  const enemyTitle = enemy.title
  const enemyName = enemy.name
  return `${enemyName} (${enemyTitle}) is an enemy to ${protagonistName} in "${title}"`
}
参数默认值
这是我经常使用的另外一个特性。这是为你的函数设定默认值的一种非常有表达力的一种声明方式。
// add(1)
// add(1, 2)
function add(a, b = 0) {
  return a + b
}
// is the same as
const add = (a, b = 0) => a + b
// is the same as
function add(a, b) {
  b = b === undefined ? 0 : b
  return a + b
}
// in React:
function useLocalStorageState({
  key,
  initialValue,
  serialize = v => v,
  deserialize = v => v,
}
)
{
  const [state, setState] = React.useState(
    () => deserialize(window.localStorage.getItem(key)) || initialValue,
  )
  const serializedState = serialize(state)
  React.useEffect(() => {
    window.localStorage.setItem(key, serializedState)
  }, [key, serializedState])
  return [state, setState]
}
Rest/Spread
...语法可以被认为是一种“集合”语法,因为它对值集合进行操作。我经常使用这个特性,而且强烈推荐你学习在哪些地方如何使用它。它确实在不同的上下文中表示不同的意思,因此学习其中的细微差别将会帮到你。
const arr = [5, 6, 8, 4, 9]
Math.max(...arr)
// is the same as
Math.max.apply(null, arr)
const obj1 = {
  a: 'a from obj1',
  b: 'b from obj1',
  c: 'c from obj1',
  d: {
    e: 'e from obj1',
    f: 'f from obj1',
  },
}
const obj2 = {
  b: 'b from obj2',
  c: 'c from obj2',
  d: {
    g: 'g from obj2',
    h: 'g from obj2',
  },
}
console.log({...obj1, ...obj2})
// is the same as
console.log(Object.assign({}, obj1, obj2))
function add(first, ...rest) {
  return rest.reduce((sum, next) => sum + next, first)
}
// is the same as
function add() {
  const first = arguments[0]
  const rest = Array.from(arguments).slice(1)
  return rest.reduce((sum, next) => sum + next, first)
}
// in React:
function Box({className, ...restOfTheProps}) {
  const defaultProps = {
    className: `box ${className}`,
    children: 'Empty box',
  }
  return <div {...defaultProps} {...restOfTheProps} />
}
  ES 模块  
如果你使用现代工具构建一个应用程序,它很可能支持模块,那么学习模块是如何生效的就是一个不错的主意,因为任何应用程序,即使是规模非常微小,也很可能需要使用模块来进行代码组织和复用。
export default function add(a, b) {
  return a + b
}
/*
 * import add from './add'
 * console.assert(add(3, 2) === 5)
 */

export const foo = 'bar'
/*
 * import {foo} from './foo'
 * console.assert(foo === 'bar')
 */

export function subtract(a, b) {
  return a - b
}
export const now = new Date()
/*
 * import {subtract, now} from './stuff'
 * console.assert(subtract(4, 2) === 2)
 * console.assert(now instanceof Date)
 */

// in React:
import React, {Suspense, Fragment} from 'react'

我对这个语法进行了全面的讲解,你可以在此观看: https://www.youtube.com/watch?v=kTlcu16rSLc&list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf。

三元条件表达式
我喜欢三元条件表达式,因为它们的声明方式太美了,特别是在 JSX 中。
onst message = bottle.fullOfSoda
  ? 'The bottle has soda!'
  : 'The bottle may not have soda :-('
// is the same as
let message
if (bottle.fullOfSoda) {
  message = 'The bottle has soda!'
} else {
  message = 'The bottle may not have soda :-('
}
// in React:
function TeddyBearList({teddyBears}) {
  return (
    <React.Fragment>
      {teddyBears.length ? (
        <ul>
          {teddyBears.map(teddyBear => (
            <li key={teddyBear.id}>
              <span>{teddyBear.name}</span>
            </li>
          ))}
        </ul>
      ) : (
        <div>There are no teddy bears. The sadness.</div>
      )}
    </React.Fragment>
  )
}

我知道三元条件表达式会受到一些人的反感,他们不得不忍受在 prettier 清理我们的代码之前理解三元条件表达式。如果你还没有使用 prettier,我强烈建议你使用。Prettier 会让你的三元条件表达式更易读。

数组方法
数组太棒了,我一直使用数组方法!我可能会经常使用如下方法:
  • find
  • some
  • every
  • includes
  • map
  • filter
  • reduce

下面是一些例子:
const dogs = [
  {
    id: 'dog-1',
    name: 'Poodle',
    temperament: [
      'Intelligent',
      'Active',
      'Alert',
      'Faithful',
      'Trainable',
      'Instinctual',
    ],
  },
  {
    id: 'dog-2',
    name: 'Bernese Mountain Dog',
    temperament: ['Affectionate', 'Intelligent', 'Loyal', 'Faithful'],
  },
  {
    id: 'dog-3',
    name: 'Labrador Retriever',
    temperament: [
      'Intelligent',
      'Even Tempered',
      'Kind',
      'Agile',
      'Outgoing',
      'Trusting',
      'Gentle',
    ],
  },
]
dogs.find(dog => dog.name === 'Bernese Mountain Dog')
// {id: 'dog-2', name: 'Bernese Mountain Dog', ...etc}
dogs.some(dog => dog.temperament.includes('Aggressive'))
// false
dogs.some(dog => dog.temperament.includes('Trusting'))
// true
dogs.every(dog => dog.temperament.includes('Trusting'))
// false
dogs.every(dog => dog.temperament.includes('Intelligent'))
// true
dogs.map(dog => dog.name)
// ['Poodle', 'Bernese Mountain Dog', 'Labrador Retriever']
dogs.filter(dog => dog.temperament.includes('Faithful'))
// [{id: 'dog-1', ..etc}, {id: 'dog-2', ...etc}]
dogs.reduce((allTemperaments, dog) => {
  return [...allTemperaments, ...dog.temperaments]
}, [])
// [ 'Intelligent', 'Active', 'Alert', ...etc ]
// in React:
function RepositoryList({repositories, owner}) {
  return (
    <ul>
      {repositories
        .filter(repo => repo.owner === owner)
        .map(repo => (
          <li key={repo.id}>{repo.name}</li>
        ))}
    </ul>

  )
}
Promises 和 async/await

这是一个很大的主题,它可能需要花费一些时间练习和使用它们才能用好它们。Promises 在 JavaScript 生态系统中无处不在,而且由于 React 在这个系统中的牢固地位,因此 Promises 在 React 中也无处不在(事实上,React 内部本身也使用 promises)。

Promises 帮你处理异步代码,并从许多 DOM API 和第三方库中解脱出来。Async/await 是处理 promises 的一种特殊语法。这两者相辅相成。
function promises() {
  const successfulPromise = timeout(100).then(result => `success: ${result}`)
  const failingPromise = timeout(200, true).then(null, error =>
    Promise.reject(`failure: ${error}`),
  )
  const recoveredPromise = timeout(300, true).then(null, error =>
    Promise.resolve(`failed and recovered: ${error}`),
  )
  successfulPromise.then(log, logError)
  failingPromise.then(log, logError)
  recoveredPromise.then(log, logError)
}
function asyncAwaits() {
  async function successfulAsyncAwait() {
    const result = await timeout(100)
    return `success: ${result}`
  }
  async function failedAsyncAwait() {
    const result = await timeout(200, true)
    return `failed: ${result}`
  }
  async function recoveredAsyncAwait() {
    let result
    try {
      result = await timeout(300, true)
      return `failed: ${result}` // this would not be executed
    } catch (error) {
      return `failed and recovered: ${error}`
    }
  }
  successfulAsyncAwait().then(log, logError)
  failedAsyncAwait().then(log, logError)
  recoveredAsyncAwait().then(log, logError)
}
function log(...args) {
  console.log(...args)
}
function logError(...args) {
  console.error(...args)
}
// This is the mothership of all things asynchronous
function timeout(duration = 0, shouldReject = false) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldReject) {
        reject(`rejected after ${duration}ms`)
      } else {
        resolve(`resolved after ${duration}ms`)
      }
    }, duration)
  })
}
// in React:
function GetGreetingForSubject({subject}) {
  const [isLoading, setIsLoading] = React.useState(false)
  const [error, setError] = React.useState(null)
  const [greeting, setGreeting] = React.useState(null)
  React.useEffect(() => {
    async function fetchGreeting() {
      try {
        const response = await window.fetch('https://example.com/api/greeting')
        const data = await response.json()
        setGreeting(data.greeting)
      } catch (error) {
        setError(error)
      } finally {
        setIsLoading(false)
      }
    }
    setIsLoading(true)
    fetchGreeting()
  }, [])
  return isLoading ? (
    'loading...'
  ) : error ? (
    'ERROR!'
  ) : greeting ? (
    <div>
      {greeting} {subject}
    </div>

  ) : null
}
    结论    

在构建 React 应用程序时,当然还有许多有用的语言特性,但是这些是我最喜欢并且自己一直重复使用的一些特性。我希望这些对你也有用。

如果你希望深入了解这些特性,我有一个我在 PayPal 工作时主持和记录的 JavaScript 研讨会,可能会对你有所帮助: https://www.youtube.com/playlist?list=PLV5CVI1eNcJgUA2ziIML3-7sMbS7utie5

英文原文: https://kentcdodds.com/blog/javascript-to-know-for-react

以上是关于React开发需要熟悉的JavaScript特性的主要内容,如果未能解决你的问题,请参考以下文章

react新特性 react hooks

怎么才能到KnownsecFED前端团队实习?

React之JSX语法

开发React.js时,需要知道的ES6的新特性

在开始React之前,你需要学好这些JavaScript

2022前端技术栈