Вопросы про React на собеседовании


Перевод статьи React Interview Questions

От переводчика: Не судите строго, так как переводил больше для себя. Если кому-то пригодиться, то хорошо.


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


Вопросы про React на собеседовании

Что происходит, когда вы вызываете setState?

Во-первых, если был вызван setState, то React захочет объединить объект, который вы отправили в setState с текущим состоянием компонента. Это позволит начать процесс согласования. Конечной целью согласования является обновление пользовательского интерфейса на основе его нового состояния наиболее эффективным из возможных способов. Для этого React сформирует новое дерево элементов (которое можно рассматривать как объектное представление вашего пользовательского интерфейса). Как только React получает это дерево, чтобы выяснить как интерфейс должен измениться в ответ на новое состояние, React сравнит новое дерево с предыдущим деревом элементов. В этом процессе, React узнает точные изменения, которые произошли. Зная эти изменения, React получит возможность провести изменения в интерфейсе только там, где это необходимо и этим минимизировать количество изменений.

Какая разница между Элементом и Компонентом в React?

Проще говоря, элемент в React описывает то, что вы хотите видеть на экране. Если не проще, то элемент в React является объектным представлением некого пользовательского интерфейса.

Компонент в React является функцией или классом, который при необходимости принимает данные и возвращает элемент (обычно в виде JSX, который преобразуется в вызов createElement)

Для дополнительной информации изучите раздел React Elements vs React Components

Когда вам использовать Class Component вместо Functional Component?

Если ваш компонент имеет состояние или содержим используемые методы значимые для компонента, то используйте Class component. В других случаях используйте Functional component.

Что за refs в React и в чем их важность?

Refs являются запасным выходом, который позволяет вам получить прямой доступ к элементу DOM или состоянию компонента. Чтобы их использовать вы добавляете атрибут ref в ваш компонент, значение которого является функцией обратного вызова, которая получит базовый элемент DOM или установленный экземпляр компонента в качестве первого аргумента.

class UnControlledForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} />
<button type='submit'>Submit</button>
</form>
)
}
}

Отметим, что наше поле ввода имеет атрибут ref, значение которого является функцией. Эта функция получает реальный элемент DOM на вход, который мы затем вставим в экземпляр для того, чтобы получить доступ к нему внутри функции handleSubmit.

Часто неверно полагают, что необходимо использовать класс компонента для того, чтобы использовать refs, но refs могут быть использованы с функциональными компонентами за счет использования замыкания:

function CustomForm ({handleSubmit}) {
let inputElement
return (
<form onSubmit={() => handleSubmit(inputElement.value)}>
<input
type='text'
ref={(input) => inputElement = input} />
<button type='submit'>Submit</button>
</form>
)
}
Что за keys в React и чем их важность?

Keys (ключи) помогают React отследить какие элементы были изменены, добавлены или удалены из списка.

render () {
return (
<ul>
{this.state.todoItems.map(({task, uid}) => {
return <li key={uid}>{task}</li>
})}
</ul>
)
}

Важно, чтобы каждый ключ был уникальным между “собратьями”. Мы уже говорили о процессе согласования и в частности о процессе сравнения нового дерева элементов с предыдущим. Keys делают этот процесс более эффективным при работе со списками, потому что React может использовать ключ на дочерний элемент, чтобы быстро узнать если элемент является новым или если он был просто перемещен при сравнении деревьев элементов. Не только keys делают этот процесс более эффективным, но без них, React не сможет узнать какое локальное состояние соответствует какому пункту при его перемещении. Поэтому не пренебрегайте использованием keys при применении map.

Если вы создали в React элемент Twitter как в примере ниже, то как бы он выглядел?
<Twitter username='tylermcginnis33'>
{(user) => user === null
? <Loading />
: <Badge info={user} />}
</Twitter>
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
// fetchUser принимает имя пользователя и возвращает promise
// который резолвится с данными пользователя
class Twitter extends Component {
// закончите код здесь
}

Если вы не очень хорошо знакомы с шаблоном render callback, это может показаться немного странным. В этом шаблоне компонент получает функцию в качестве своего потомка. Обратите внимание, что находится внутри тега <Twitter> выше. Вместо другого компонента, как вы возможно видели до этого, потомок компонента Twitter является функцией. Это означает то, что в данной реализации компонента Twitter нам необходимо обратиться к props.children как к функции.

Вот как я вижу это решение:

import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
class Twitter extends Component {
state = {
user: null,
}
static propTypes = {
username: PropTypes.string.isRequired,
}
componentDidMount () {
fetchUser(this.props.username)
.then((user) => this.setState({user}))
}
render () {
return this.props.children(this.state.user)
}
}

Обратите внимание, что, как упоминал выше, я обращаюсь к props.children как к функции, вызывая ее и передавая пользователя.

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

Чтобы продемонстрировать это, давайте примем, что в другом файле мы хотим отрисовать Profile вместо Badge, и так как мы используем шаблон render callback, мы можем менять окружение интерфейса без изменения нашей реализации родительского компонента Twitter.

<Twitter username='tylermcginnis33'>
{(user) => user === null
? <Loading />
: <Profile info={user} />}
</Twitter>
В чем разница между controlled и uncontrolled компонентами?

Одна из основных идей React, это наличие контроля над компонентами и управление их собственным состоянием. Что случится если мы отправим чистый HTML элементов формы (input, select, textarea и т.д.) в общей куче? Должны ли мы иметь React, как “единственный источник правды” или мы должны позволить, чтобы данные формы существовали в DOM в HTML-форме? Эти два вопроса лежат в основе контролируемых (controlled) и неконтролируемых (uncontrolled) компонентов.

Контролируемый компонент?—?это такой компонент, где React осуществляет контроль и является единственным источником правды для данных формы. Как вы можете видеть ниже, username существует не в DOM, а нашем состоянии компонента. Всякий раз, когда хотим обновить username, мы вызываем setState, как мы уже привыкли.

class ControlledForm extends Component {
state = {
username: ''
}
updateUsername = (e) => {
this.setState({
username: e.target.value,
})
}
handleSubmit = () => {}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.username}
onChange={this.updateUsername} />
<button type='submit'>Submit</button>
</form>
)
}
}

Некотролируемый компонент?—?это такой компонент, где ваши данные формы обрабатываются в DOM, а не внутри вашего компонента.

class UnControlledForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={(input) => this.input = input} />
<button type='submit'>Submit</button>
</form>
)
}
}

Хотя неконтролируемые компоненты обычно проще в реализации, так как вы просто берете значение из DOM используя refs, рекомендуется использовать контролируемые компоненты. Основная причина этого в том, что контролируемые компоненты поддерживают мгновенную проверку полей, позволяют вам условно отключать/включать кнопки, устанавливать формат входных данных и вообще следует более сути React.

В какой момент жизненного цикла вы применяется AJAX запросы и почему?

AJAX запросы должны идти в момент события componentDidMount.

Для этого есть несколько причин:

  • Следующая реализация алгоритма сверки в React будет иметь возможность запускать и останавливать рендеринг для повышения производительности. Одним из компромиссов является то, что componentWillMount, другой цикл событий, где, возможно, стоит реализовать AJAX-запрос, будет “не детерминированным”. Это означает то, то React может начать вызывать componentWillMount в разное время, когда он чувствует в этом необходимость. Это, очевидно, плохая формула для создания AJAX-запроса.
  • Вы не можете гарантировать, что AJAX-запрос не будет разрешен (resolve) перед моментом монтирования компонента. Если да, то это будет означать, что вы пытаетесь выполнить setState над демонтированным компонентом, который не только не работает, но и сам React будет ругаться на вас. Делайте AJAX-запросы в componentDidMount, чтобы гарантировать, что есть компонент для обновления.
Что делает и почему важен shouldComponentUpdate?

Выше мы уже говорили о сверке и что делает React когда вызван setState. Что делает shouldComponentUpdate, это метод жизненного цикла, который позволяет нам от процесса сравнения для некоторых компонентов (и их дочерних компонентов). Зачем нам это вообще нужно делать? Как отмечалось выше, “цель сравнения в том, чтобы самым эффективным путем обновить интерфейс на основе нового состояния”. Если вы знаете, что часть интерфейса не изменится, то нет причин, заставлять React пройти через трудности, чтобы понять это. При возвращения false из shouldComponentUpdate, React будет понимать, что текущий компонент и все его дочерние компоненты останутся такими же, какими являются в данный момент.

Как вы скажете React строить в режиме Production и как это сделать?

Обычно вы можете использовать метод DefinePlugin в Webpack для установки NODE_ENV в production. Это вырежет такие вещи, как propType валидацию и другие предупреждения. Кроме того, это хорошая идея, чтобы минимизировать ваш код, потому что React использует Uglify для удаления “мертвого кода” в виде самого кода и комментариев, что позволит сильно уменьшить размер сборки.

Почему необходимо использовать React.Children.map(props.children, () => ) вместо props.children.map(() => ) ?

Нет гарантии, что props.children будет массивом.

Взгляните на код:

<Parent>
<h1>Welcome.</h1>
</Parent>

Если внутри Parent в попытаетесь вызвать props.children.map это вызовет ошибку, потому что props.children является объектом, а не массивом. 

React отработает с props.children только в том случае, если родитель имеет более одного дочернего элемента, как здесь:

<Parent>
<h1>Welcome.</h1>
<h2>props.children will now be an array</h2>
</Parent>

Вы должны использовать React.Children.map, потому что эта реализация учитывает, что props.children может быть как объектом, так и массивом.

Опишите, как в React обрабатываются события?

Чтобы решить проблемы кроссбраузерной совместимости, вашим обработчикам в React будут переданы экземпляры SyntheticEvent, которые являются в React обертками над нативными событиями браузеров. Эти искусственные события имеют такой же интерфейс как и у нативных методов, которые вы используете, за исключение того, что они одинаково хорошо работают во всех браузерах.

Интересно то, что React не назначает события на дочерние элементы сам. React будет слушать все события на верхнем уровне, используя простой слушатель событий. Это хорошо для производительности и также означает, что React не нужно беспокоиться об отслеживании при обновлении DOM.

В чем разница между createElement и cloneElement?

createElement мы получаем из JSX и его React использует для создания элементов (объектное представление некоторого интерфейса). cloneElement используется для клонирования элемента и отправить ему новые параметры. 

Какой второй аргумент можно передать опционально в setState и какова его цель?

Функция обратного вызова, которая будет вызываться после выполнения функции setState и отрисовки компонента.

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

this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
Что не так с этим кодом?
this.setState((prevState, props) => {
return {
streak: prevState.streak + props.count
}
})

С кодом все отлично :-) Такой вариант редко используется и не достаточно хорошо известен, но вы можете отправить функцию в setState, которая получает предыдущее состояние и параметры props и возвращает новое состояние, также как мы делали выше. И это не только не плохой код, а, более того, является рекомендуемым если вы получаете новое состояние на основании предыдущего.

15.01.2017 Эту страницу просмотрели за все время 10075 раз(а)


ВКонтакте



Twitter


Облако тегов