使用 React 要懂的 JavaScript 特性[每日前端夜话0xB3]
与我使用的其他框架相比,我最喜欢 React 的原因之一就是它对 JavaScript 的暴露程度。没有模板DSL( JSX 编译为合理的 JavaScript),组件 API 只是通过添加 React Hooks 变得更简单,并且该框架为解决的核心 UI 问题提供非常少的抽象概念。
因此,学习 JavaScript 对于使用 React 有效构建应用程序是非常可取的。所以这里有一些 JavaScript 功能,我建议你花一些时间学习,这样你就可以尽可能有效地使用 React。
模板文字
模板文字就像具有超能力的字符串:
1const greeting = 'Hello' 2const subject = 'World' 3console.log(`${greeting} ${subject}!`) // Hello World! 4 5// this is the same as: 6console.log(greeting + ' ' + subject + '!') 7 8// in React: 9function Box({className, ...props}) {10 return <div className={`box ${className}`} {...props} />11}12
MDN:模板文字(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)
简写属性名
很常见并且有用,我直到现在都没有想到可以这样做。
1const a = 'hello' 2const b = 42 3const c = {d: [true, false]} 4console.log({a, b, c}) 5 6// this is the same as: 7console.log({a: a, b: b, c: c}) 8 9// in React:10function Counter({initialCount, step}) {11 const [count, setCount] = useCounter({initialCount, step})12 return <button onClick={setCount}>{count}</button>13}
MDN:ECMAScript 2015中对象初始化的新表示法(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015)
箭头函数
箭头函数是在 JavaScript 中另一种编写函数的方法,但它们确实存在一些语义差异。幸运的是我们在 React 的土地上,如果在项目中使用hook(而不是类)就不必担心 this,但是箭头函数允许更复杂的匿名函数和隐式返回,所以你会看到并想要充分利用箭头的功能。
1const getFive = () => 5 2const addFive = a => a + 5 3const divide = (a, b) => a / b 4 5// this is the same as: 6function getFive() { 7 return 5 8} 9function addFive(a) {10 return a + 511}12function divide(a, b) {13 return a / b14}1516// in React:17function TeddyBearList({teddyBears}) {18 return (19 <ul>20 {teddyBears.map(teddyBear => (21 <li key={teddyBear.id}>22 <span>{teddyBear.name}</span>23 </li>24 ))}25 </ul>26 )27}
MDN:箭头函数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
解构
解构可能是我最喜欢的 JavaScript 功能。我一直在构造对象和数组(如果你使用 useState,可能也是如此,就像这样【https://kentcdodds.com/blog/react-hooks-array-destructuring-fundamentals】)。我喜欢它的陈述性。
1// const obj = {x: 3.6, y: 7.8} 2// makeCalculation(obj) 3 4function makeCalculation({x, y: d, z = 4}) { 5 return Math.floor((x + d + z) / 3) 6} 7 8/ this is the same as 9function makeCalculation(obj) {10 const {x, y: d, z = 4} = obj11 return Math.floor((x + d + z) / 3)12}1314// which is the same as15function makeCalculation(obj) {16 const x = obj.x17 const d = obj.y18 const z = obj.z === undefined ? 4 : obj.z19 return Math.floor((x + d + z) / 3)20}2122// in React:23function UserGitHubImg({username = 'ghost', ...props}) {24 return <img src={`https://github.com/${username}.png`} {...props} />25}26
MDN:解构分配(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
一等要去阅读 MDN 文章,你肯定能够学到新东西。当你完成后,尝试用单行解构:
1function nestedArrayAndObject() { 2 // refactor this to a single line of destructuring... 3 const info = { 4 title: 'Once Upon a Time', 5 protagonist: { 6 name: 'Emma Swan', 7 enemies: [ 8 {name: 'Regina Mills', title: 'Evil Queen'}, 9 {name: 'Cora Mills', title: 'Queen of Hearts'},10 {name: 'Peter Pan', title: `The boy who wouldn't grow up`},11 {name: 'Zelena', title: 'The Wicked Witch'},12 ],13 },14 }15 // const {} = info // <-- replace the next few `const` lines with this16 const title = info.title17 const protagonistName = info.protagonist.name18 const enemy = info.protagonist.enemies[3]19 const enemyTitle = enemy.title20 const enemyName = enemy.name21 return `${enemyName} (${enemyTitle}) is an enemy to ${protagonistName} in "${title}"`22}
参数默认值
这是另一个我一直在用的功能:一种以声明方式表达函数默认值的非常强大的方法。
1// add(1) 2// add(1, 2) 3function add(a, b = 0) { 4 return a + b 5} 6 7// is the same as 8const add = (a, b = 0) => a + b 910// is the same as11function add(a, b) {12 b = b === undefined ? 0 : b13 return a + b14}1516// in React:17function useLocalStorageState({18 key,19 initialValue,20 serialize = v => v,21 deserialize = v => v,22}) {23 const [state, setState] = React.useState(24 () => deserialize(window.localStorage.getItem(key)) || initialValue,25 )2627 const serializedState = serialize(state)28 React.useEffect(() => {29 window.localStorage.setItem(key, serializedState)30 }, [key, serializedState])3132 return [state, setState]33}
MDN:默认参数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters)
Rest/Spread
...语法可以被认为是一种“集合”语法,它在一组值上运行。我一直都在使用,强烈建议你也学习。它实际上在不同的环境中有不同的含义,因此学习那些细微差别会对你有所帮助。
1const arr = [5, 6, 8, 4, 9] 2Math.max(...arr) 3// is the same as 4Math.max.apply(null, arr) 5 6const obj1 = { 7 a: 'a from obj1', 8 b: 'b from obj1', 9 c: 'c from obj1',10 d: {11 e: 'e from obj1',12 f: 'f from obj1',13 },14}15const obj2 = {16 b: 'b from obj2',17 c: 'c from obj2',18 d: {19 g: 'g from obj2',20 h: 'g from obj2',21 },22}23console.log({...obj1, ...obj2})24// is the same as25console.log(Object.assign({}, obj1, obj2))2627function add(first, ...rest) {28 return rest.reduce((sum, next) => sum + next, first)29}30// is the same as31function add() {32 const first = arguments[0]33 const rest = Array.from(arguments).slice(1)34 return rest.reduce((sum, next) => sum + next, first)35}3637// in React:38function Box({className, ...restOfTheProps}) {39 const defaultProps = {40 className: `box ${className}`,41 children: 'Empty box',42 }43 return <div {...defaultProps} {...restOfTheProps} />44}45
MDN:Spread语法(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
MDN:Rest 参数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters)
ESModules
如果你正在使用现代工具构建自己的程序,它应该能够支持模块,了解语法怎样工作是个好主意,因为所有的甚至微不足道的程序都可能需要使用模块来重用代码。
1export default function add(a, b) { 2 return a + b 3} 4 5/* 6 * import add from './add' 7 * console.assert(add(3, 2) === 5) 8 */ 910export const foo = 'bar'1112/*13 * import {foo} from './foo'14 * console.assert(foo === 'bar')15 */1617export function subtract(a, b) {18 return a - b19}2021export const now = new Date()2223/*24 * import {subtract, now} from './stuff'25 * console.assert(subtract(4, 2) === 2)26 * console.assert(now instanceof Date)27 */2829// in React:30import React, {Suspense, Fragment} from 'react'
MDN:import(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
MDN:export(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export)
三元表达式
我喜欢三元表达式。他们的声明很漂亮。特别是在 JSX 中。
1const message = bottle.fullOfSoda 2 ? 'The bottle has soda!' 3 : 'The bottle may not have soda :-(' 4 5// is the same as 6let message 7if (bottle.fullOfSoda) { 8 message = 'The bottle has soda!' 9} else {10 message = 'The bottle may not have soda :-('11}1213// in React:14function TeddyBearList({teddyBears}) {15 return (16 <React.Fragment>17 {teddyBears.length ? (18 <ul>19 {teddyBears.map(teddyBear => (20 <li key={teddyBear.id}>21 <span>{teddyBear.name}</span>22 </li>23 ))}24 </ul>25 ) : (26 <div>There are no teddy bears. The sadness.</div>27 )}28 </React.Fragment>29 )30}31
我意识到,在 prettier 出现并清理我们的代码之前,一些人不得不花时间弄清楚三元运算符是怎么回事,这让三元表达式变得令人反感。如果你还没有使用 prettier,我强烈建议你这样做。prettier 将使你的三元表达式更容易阅读。
MDN:条件(三元)运算符(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)
数组方法
数组很棒,我一直使用数组方法!以下是我常用的方法:
find
some
every
includes
map
filter
- reduce
这里有些例子:
1const dogs = [ 2 { 3 id: 'dog-1', 4 name: 'Poodle', 5 temperament: [ 6 'Intelligent', 7 'Active', 8 'Alert', 9 'Faithful',10 'Trainable',11 'Instinctual',12 ],13 },14 {15 id: 'dog-2',16 name: 'Bernese Mountain Dog',17 temperament: ['Affectionate', 'Intelligent', 'Loyal', 'Faithful'],18 },19 {20 id: 'dog-3',21 name: 'Labrador Retriever',22 temperament: [23 'Intelligent',24 'Even Tempered',25 'Kind',26 'Agile',27 'Outgoing',28 'Trusting',29 'Gentle',30 ],31 },32]3334dogs.find(dog => dog.name === 'Bernese Mountain Dog')35// {id: 'dog-2', name: 'Bernese Mountain Dog', ...etc}3637dogs.some(dog => dog.temperament.includes('Aggressive'))38// false3940dogs.some(dog => dog.temperament.includes('Trusting'))41// true4243dogs.every(dog => dog.temperament.includes('Trusting'))44// false4546dogs.every(dog => dog.temperament.includes('Intelligent'))47// true4849dogs.map(dog => dog.name)50// ['Poodle', 'Bernese Mountain Dog', 'Labrador Retriever']5152dogs.filter(dog => dog.temperament.includes('Faithful'))53// [{id: 'dog-1', ..etc}, {id: 'dog-2', ...etc}]5455dogs.reduce((allTemperaments, dog) => {56 return [...allTemperaments, ...dog.temperaments]57}, [])58// [ 'Intelligent', 'Active', 'Alert', ...etc ]5960// in React:61function RepositoryList({repositories, owner}) {62 return (63 <ul>64 {repositories65 .filter(repo => repo.owner === owner)66 .map(repo => (67 <li key={repo.id}>{repo.name}</li>68 ))}69 </ul>70 )71}
MDN:Array(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
Promises 和 async/await
这是一个很大的主题,可以在它们身上多花一些时间。Promises 在 JavaScript 生态中无处不在,并且由于 React在该生态系统中的根深蒂固,它们几乎到处都是(事实上,React 本身在内部也在使用 promises)。
Promises 可帮助你管理异步代码。Async/await 语法是处理 promises 的特殊语法。这两者是相辅相成的。
1function promises() { 2 const successfulPromise = timeout(100).then(result => `success: ${result}`) 3 4 const failingPromise = timeout(200, true).then(null, error => 5 Promise.reject(`failure: ${error}`), 6 ) 7 8 const recoveredPromise = timeout(300, true).then(null, error => 9 Promise.resolve(`failed and recovered: ${error}`),10 )1112 successfulPromise.then(log, logError)13 failingPromise.then(log, logError)14 recoveredPromise.then(log, logError)15}1617function asyncAwaits() {18 async function successfulAsyncAwait() {19 const result = await timeout(100)20 return `success: ${result}`21 }2223 async function failedAsyncAwait() {24 const result = await timeout(200, true)25 return `failed: ${result}`26 }2728 async function recoveredAsyncAwait() {29 let result30 try {31 result = await timeout(300, true)32 return `failed: ${result}` // this would not be executed33 } catch (error) {34 return `failed and recovered: ${error}`35 }36 }3738 successfulAsyncAwait().then(log, logError)39 failedAsyncAwait().then(log, logError)40 recoveredAsyncAwait().then(log, logError)41}4243function log(...args) {44 console.log(...args)45}4647function logError(...args) {48 console.error(...args)49}5051// This is the mothership of all things asynchronous52function timeout(duration = 0, shouldReject = false) {53 return new Promise((resolve, reject) => {54 setTimeout(() => {55 if (shouldReject) {56 reject(`rejected after ${duration}ms`)57 } else {58 resolve(`resolved after ${duration}ms`)59 }60 }, duration)61 })62}6364// in React:65function GetGreetingForSubject({subject}) {66 const [isLoading, setIsLoading] = React.useState(false)67 const [error, setError] = React.useState(null)68 const [greeting, setGreeting] = React.useState(null)6970 React.useEffect(() => {71 async function fetchGreeting() {72 try {73 const response = await window.fetch('https://example.com/api/greeting')74 const data = await response.json()75 setGreeting(data.greeting)76 } catch (error) {77 setError(error)78 } finally {79 setIsLoading(false)80 }81 }82 setIsLoading(true)83 fetchGreeting()84 }, [])8586 return isLoading ? (87 'loading...'88 ) : error ? (89 'ERROR!'90 ) : greeting ? (91 <div>92 {greeting} {subject}93 </div>94 ) : null95}
MDN:Promise(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
MDN:async function(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
MDN:await(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
结论
当然有许多语言功能在构建 React 应用时很有用,这些是我最喜欢的,我发现自己一次又一次地使用它们。希望对你有帮助。
原文:https://kentcdodds.com/blog/javascript-to-know-for-react
©著作权归作者所有:来自51CTO博客作者mb5ff980b461ced的原创作品,如需转载,请注明出处,否则将追究法律责任更多相关文章
- 用 await/async 正确链接 Javascript 中的多个函数[每日前端夜话
- 用原生 JavaScript 实现十大 jQuery 函数[每日前端夜话0x94]
- shell脚本之灵活调用函数技巧
- 函数式编程思维在三行代码情书中的应用
- 关于 Hive开窗函数
- string.h中部分函数的实现
- 学习C的第三天-函数
- SQL今日一题(11):窗口函数
- 为什么说 Python 内置函数并不是万能的?