JavaScript, который нужно знать для React

JavaScript, который нужно знать для React

От автора: одна из вещей, которые мне больше всего нравятся в 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} />
}

MDN: литералы шаблонов

Сокращенные имена свойств

Это настолько распространено и полезно, что я делаю это сейчас, не задумываясь.

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.)

MDN: стрелочные функции

Деструктурирование

Деструктурирование, вероятно, моя любимая функция 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]
}

MDN: параметры по умолчанию

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} />
}

MDN: синтаксис Spread

MDN: параметры Rest

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'

MDN: import

MDN: export

В качестве другого ресурса я сделал полный доклад об этом синтаксисе, и вы можете посмотреть этот доклад здесь.

Тернарные операторы

Я люблю тернарные операторы. Они прекрасно декларативны. Особенно в 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> )
}

MDN: массивы

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
}

MDN: Promise

MDN: функции async

MDN: await

Заключение

Конечно, есть много функций языка, которые полезны при создании приложений React, но это некоторые из моих любимых, которые я использую постоянно. Я надеюсь, что вы найдете это полезным. Удачи!

Автор: Kent C. Dodds

Источник: https://kentcdodds.com

Редакция: Команда webformyself.