От автора: одна из вещей, которые мне больше всего нравятся в React по сравнению с другими фреймворками, которые я использовал, это то, насколько вы открыты для JavaScript при его использовании. Шаблон DSL (JSX компилируется в разумный JavaScript) отсутствует, API компонентов стал проще с добавлением React Hooks, и фреймворк предлагает вам очень мало абстракций вне основных проблем пользовательского интерфейса, которые он призван решить.
Из-за этого, изучение функций JavaScript действительно целесообразно для эффективной разработки приложений с помощью React. Итак, вот несколько функций JavaScript, которые я бы порекомендовал вам изучить, чтобы вы могли быть максимально эффективными при работе с React.
Литералы шаблонов
Литералы шаблонов похожи на обычные строки со сверхспособностями:
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> }
MDN: инициализатор объекта Новые записи в ECMAScript 2015
Стрелочные функции
Стрелочные функции — это еще один способ написания функций в JavaScript, но они имеют несколько семантических различий. К счастью для нас в React, нам не нужно так много беспокоиться о this, если мы используем в проекте хуки (а не классы), но стрелочные функции позволяют использовать более анонимные функции и неявные возвраты, так что вы часто захотите использовать стрелочные функции.
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. Я деструктурирую объекты и массивы все время. Мне нравится, как это декларативно.
// 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: назначение деструктурирования
Обязательно прочитайте эту статью на 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} /> }
ESModules
Если вы создаете приложение с современными инструментами, скорее всего, оно поддерживает модули, хорошая идея узнать, как работает синтаксис, потому что любое приложение даже тривиального размера, вероятно, будет нуждаться в применении модулей для повторного использования и организации кода.
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'
В качестве другого ресурса я сделал полный доклад об этом синтаксисе, и вы можете посмотреть этот доклад здесь.
Тернарные операторы
Я люблю тернарные операторы. Они прекрасно декларативны. Особенно в JSX.
const 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, я настоятельно советую вам это сделать. Они сделают ваши тернарные операторы намного читаемее.
MDN: условные (тернарные) операторы
Методы массива
Массивы просто фантастические, и я постоянно использую методы массивов! Чаще всего я, пожалуй, использую следующие методы:
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> ) }
Promise и async/await
Это большая тема, и может потребоваться немало практики и времени, чтобы проработать ее. Promise встречаются повсюду в экосистеме JavaScript, и благодаря тому, как React укоренился в этой экосистеме, они также присутствуют везде (фактически, сам React использует promise внутри).
Promise помогают вам управлять асинхронным кодом и возвращаются из многих DOM API, а также из сторонних библиотек. Синтаксис Async / await — это специальный синтаксис для работы с promise. Они неразрывно связаны.
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, но это некоторые из моих любимых, которые я использую постоянно. Я надеюсь, что вы найдете это полезным. Удачи!
Автор: Kent C. Dodds
Источник: https://kentcdodds.com
Редакция: Команда webformyself.