Собеседование #1 и работа над ошибками

Без воды
Мои скилы
React, MobX, Webpack, HTML5, CSS3, ES3, ES5, ES6, SCSS, GIT, Styled Components, NPM, JQuery
Позиция
Junior frontend developer (React, Redux)
Требования
React, Redux, GIT, NPM, HTML5, CSS3, SCSS, ES5, ES6, Webpack
Не хватает Redux…
Так как есть понимания и практика в State Managment, по этому получаю тестовое задание.
Тестовое задание


Using the create-react-app develope the React Application with two forms:
1 ) Contacts list
2) New/Edit Contact
Please consider the requirements:
1 . Do not implement any authentication
2. Provide validation for an email field
3. Host the static content at the github-pages
4. Use firebase as a backend (do not implement live data rendering on
remote data changed)
To use redux is a big plus.
Expecting for the output:
1. Link to gihub-repo with source code
2. Link to the working application hosted at the github
Результат
- Познакомился с Redux ближе
- Разобрался в тонкостях архитектуры
- Разобрался с Firebase
- Впервые использовал CRA
Техническое собеседование
5 человек:
- Рекрутер
- Тимлид
- 3 мидла
Вопросы
>> О себе?
>> Причина смены работы?
>> Знаком ли с продуктами компании?
Обязательно перед собеседованием надо изучить чем занимается компания, традиции компании.
>> Чем хотел бы заниматься в компании?
>> Event loop в JS?
Два важных момента:
- как выполняются асинхронные операции
- что происходит при выполнении setTimeout
Важно сказать за стек вызовов, очередь коллбэков. Детально ознакомиться можно в следующих материалах:
И вопросы по этой тематике:
Есть код
console.log('Hi');
setTimeout(function() {
console.log('callback');
}, 0);
console.log('Bye');В каком порядке будет вывод?
Ответ
Hi
Bye
callbackПочему? В setTimeout указано 0 милисекунд
Все очень просто.
Как работает setTimeout
По данному примеру через 0 милисекунд коллбэк только попадает в очередь коллбэков, но в очереди могут быть и другие коллбэки, по этому это не происходит мгновенно.
Так как все действия в браузере происходят в одном потоке, то очередь коллбэков ждет пока очистится коллстек. А в коллстек по данному примеру попал вывод Hi и Bye.
После того как вывело Hi и Bye коллстек пустой и в него попадает вывод callback и очередь коллбэков пустая.
Важно
Если не передавать параметр delay в функцию setTimeout это все равно что delay будет равным 0. (Тоже спрашивали)
Так же будет и со следующим примером
console.log('Hi');
setTimeout(function() {
console.log('callback 1');
});
console.log('Hi one`s more');
setTimeout(function() {
console.log('callback 2');
});
console.log('Buy');Выведет
Hi
Hi one`s more
Buy
callback 1
callback 2>> Обьяснить асинхронность в браузере?
По такому же принципу работают запросы на бэкенд и в этом и заключается асинхронность
Что касается запросов на бэкенд
Если использовать XMLHttpRequest, по умолчанию запрос будет синхронным и залипнет UI при выполнении такого запроса.
По этому для того чтобы такого не было пареметр async ставим true.
Но это старая школа в ES5 появилась функция fetch, работа которой основана на промисах (Promises)
ES6 дает нам async await
>> Что выведет следующий код ?
for(var i=0;i<10;i++) {
setTimeout(function() {
console.log(i);
}, 1000)
}Ответ
10
10
10
10
10
10
10
10
10
10Почему?
Область видимости и замыкание!!!
var обьявляет переменную глобально, а так как цикл выполнится быстрее чем будет первый вывод в консоли то соответственно значение будет 10.
Как решить ?
Заменим var на let
for(let i=0;i<10;i++) {
setTimeout(function() {
console.log(i);
}, 1000)
}Или же старым добрым ES3
for(var i=0;i<10;i++) {
setTimeout((function(x) {
console.log(x);
})(i))
}Почему так происходит?
Коллбэк создан как результат вызова промежуточного функционального выражения function(x), которое объявляется – и тут же выполняется, получая x = i.
Так как function(x) тут же завершается, то значение x больше не меняется. Оно и будет использовано в возвращаемой функции-стрелке.
Не понятно, тогда читаем о функциональных выражениях
>> Что такое замыкание ?
Спойлер

>> Пример замыкания ?
var userName = 'John';
function sayHi() {
alert(userName);
}Переменная userName берется из внешнего блока переменных, так как в своем LexicalEnvironment такой переменной нет.
>> Что такое промисы и как они работают ?
Читаем здесь + асинхронность
>> Рассказать о ES6 ?
Основные моменты:
Важно заметить что стрелочная функция не имеет своего контекста, она берет контекст с родителя.
Этого будет достаточно
>> Чем const отличается от let ?
>> Будет ли ошибка если константе задать значение обьекта и потом поменять значение одного из полей этого обьекта ?
const user = {
name: 'John'
};
user.name = 'Mark';Ошибки не будет!
Константа user получает только ссылку на обьект, так как обьекты это ссылочные типы и при изменении свойства обьекта ссылка не меняется.
Будет ошибка если константой будет скалярное значение или же строчное выражение.
>> Что такое var ?
>> Рассказать о стрелочных функциях ?
>> Рассказать что такое контекст ?
Спойлер: Значение this называется контекстом вызова и будет определено в момент вызова функции.
>> Написать свою реализацию метода bind ?
Дают ручку и листок и пишем
function bind(func, context) {
return function() {
return func.apply(context, arguments);
};
}>> Описать что происходит когда ты в браузере в адресной строке набираешь адрес и нажимаешь Enter ?
Возьмем к примеру google.com
Это URL или поисковый запрос?
Когда пользователь не вводит протокол или доменное имя, то браузер «скармливает» то, что человек напечатал, поисковой машине, установленной по умолчанию. Часто к URL добавляется специальный текст, который позволяет поисковой машине понять, что информация передана из URL-строки определённого браузера.
Список проверки HSTS
Браузер проверяет список «предзагруженных HSTS (HTTP Strict Transport Security)». Это список сайтов, которые требуют, чтобы к ним обращались только по HTTPS.
Определение DNS
Открытие сокета
TLS handshake
Обработка HTTP-запросов на сервере
— HTTPD (HTTP Daemon) получает запрос.
— Сервер разбирает запрос по следующим параметрам:
- Метод HTTP-запроса (
GET,POST,HEAD,PUTилиDELETE). В случае URL-адреса, который пользователь напечатал в строке браузера, мы имеем дело с GET-запросом. - Домен. В нашем случае — google.com.
- Запрашиваемые пути/страницы, в нашем случае —
/(нет запрошенных путей,/— это путь по умолчанию).
— Сервер проверяет существование виртуального хоста, который соответствует google.com.
— Сервер проверяет, что google.com может принимать GET-запросы.
— Сервер проверяет, имеет ли клиент право использовать этот метод (на основе IP-адреса, аутентификации и прочее).
— Если на сервере установлен модуль перезаписи (mod_rewrite для Apache или URL Rewrite для IIS), то он сопоставляет запрос с одним из сконфигурированных правил. Если находится совпадающее правило, то сервер использует его, чтобы переписать запрос.
— Сервер находит контент, который соответствует запросу, в нашем случае он изучит индексный файл.
— Далее сервер разбирает («парсит») файл с помощью обработчика. Если Google работает на PHP, то сервер использует PHP для интерпретации индексного файла и направляет результат клиенту.
Парсинг HTML
Движок рендеринга начинает получать содержимое запрашиваемого документа от сетевого механизма браузера. Как правило, контент поступает кусками по 8Кб. Главной задачей HTML-парсера является разбор разметки в специальное дерево.
Получающееся на выходе дерево («parse tree») — это дерево DOM-элементов и узлов атрибутов. DOM — сокращение от Document Object Model. Это модель объектного представления HTML-документа и интерфейс для взаимодействия HTML-элементов с «внешним миром» (например, JavaScript-кодом). Корнем дерева является объект «Документ»
Действия после завершения парсинга
После этого браузер начинает подгружать внешние ресурсы, связанные со страницей (стили, изображения, скрипты и так далее).
Важный момент: ошибки «Invalid Syntax» при разборе не может быть, поскольку браузеры исправляют любой «невалидный» контент и продолжают работу.
Интерпретация CSS
Рендеринг страниц
Путём перебора DOM-узлов и вычисления для каждого узла значений CSS-стилей создаётся «Дерево рендера» (Render Tree или Frame Tree).
Вызванное пользователем и пост-рендеринговое исполнение
После завершения рендеринга, браузер исполняет JavaScript-код в результате срабатывания некоего часового механизма (так работают дудлы на странице Google) или в результате действий пользователя (ввод поискового запроса в строку и получение рекомендаций в ответ). Также могут срабатывать плагины вроде Flash или Java (но не в рассматриваемом примере с домашней страницей Google). Скрипты могут потребовать обработки дополнительных сетевых запросов, изменять страницу или её шаблон, что приведёт к следующему этапу рендеринга и отрисовки
Читай здесь чтобы знать больше
>> Как выгружается CSS ?
- Во время разбора браузер парсит CSS-файлы, содержимое тегов
<style>и атрибутов «style» c помощью «лексической и синтаксической грамматики CSS». - Каждый CSS-файл разбирается в объект
StyleSheet, каждый из таких объектов содержит правила CSS с селекторами и объектами в соответствии с грамматикой CSS. - Парсер CSS может быть как восходящим, так и нисходящим.
Как выгружаются скрипты(js) ?
На этом этапе браузер помечает документ, как интерактивный и начинает разбирать скрипты, находящиеся в «отложенном» состоянии: то есть те из них, что должны быть исполнены после парсинга. После этого статус документа устанавливается в состояние «complete» и инициируется событие загрузки («load»).
<script async>...</script>При наличии атрибута async браузер при возможности запускает скрипт асинхронно. Это означает, что указанный в атрибуте src файл будет выполняться без ожидания загрузки и отображения веб-страницы. В то же время и страница не ожидает результата выполнения скрипта, а продолжает загружаться как обычно.
<script defer>...</script>Атрибут defer откладывает выполнение скрипта до тех пор, пока вся страница не будет загружена полностью.
>> Как собирается страница на стороне сервера ?
Вкраце:
- Сервер принимает запрос
- Определяет http метод (GET, POST, PUT, DELETE)
- Достает данные с базы данных
- Идентифицирует пользователя
- Генерирует HTML код
- Вставляет в head список js скриптов которые будут работать на стороне клиента (браузера)
- Вставляет список css файлов
- Генерирует body
- В зависимости от условий (авторизационных данных, прав доступа) вставляет необходимые теги (списки, блоки, меню) в которых есть данные полученые с базы.
Пример на PHP:
$userList = $db->getUsers(); // получаем список пользователей с БД
$title = 'Просмотр списка пользователей';
$out = '<html><head><title>' . $title . '</title></head>';
$out .= "<body><div id='users'><ul>"; // .= конкатенация строк
foreach($userList as $user) {
$out .= '<li>' . $user . '</li>';
}
$out .= '</ul></div></body></html>';
echo $out; // вывод страницы- Возвращает полный HTML код клиенту (браузеру)
>> Обьяснить всплытие и перехват событий(Bubbling and capturing)?
Всплытие
При наступлении события обработчики сначала срабатывают на самом вложенном элементе, затем на его родителе, затем выше и так далее, вверх по цепочке вложенности.
<form onclick="alert('form')">
<div onclick="alert('div')">
<p onclick="alert('p')">P</p>
</div>
</form>Всплытие гарантирует, что клик по внутреннему <p> вызовет обработчик onclick (если есть) сначала на самом <p>, затем на элементе <div> далее на элементе <form>, и так далее вверх по цепочке родителей до самого document.

Поэтому если в примере выше кликнуть на P, то последовательно выведутся alert: p → div → form.
Этот процесс называется всплытием, потому что события «всплывают» от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
Перехват
Когда собитие идет сверху вниз эта стадия называется «стадия перехвата» (capturing stage).
Событие достигло целевого элемента. Это «стадия цели» (target stage).
После этого событие начинает всплывать. Это «стадия всплытия» (bubbling stage).
То есть, при клике на P событие путешествует по цепочке родителей сначала вниз к элементу («погружается»), а потом наверх («всплывает»), по пути задействуя обработчики.
Ранее мы говорили только о всплытии, потому что другие стадии, как правило, не используются и проходят незаметно для нас.
Обработчики, добавленные через on...-свойство, ничего не знают о стадии перехвата, а начинают работать со всплытия.
Чтобы поймать событие на стадии перехвата, нужно использовать третий аргумент addEventListener:
- Если аргумент
true, то событие будет перехвачено по дороге вниз. - Если аргумент
false, то событие будет поймано при всплытии.
>> Рассказать о DOM Event и как они работают ?
- Event
- MouseEvent
- CustomEvent
- dispatchEvent
>> В чем особенность React?
Самые “тяжелые” операции в web — работа с DOM. Реакт оптимизирует эту работу. Как? Virtual DOM + обновление страницы за минимум “телодвижений”.
Новое дерево, являющееся виртуальным DOM, будет содержать новое состояние. Затем React сравнивает виртуальный DOM со старым, и вычисляет разницу между обеими DOM, после чего выполняет обновление только той части, которая отличается.
>> setState сразу обновляет состояние ?
setState (документация) — асинхронная функция. Чтобы выполнить, что-либо заведомо после обновления state, нужно использовать запись с callback’ом (второй параметр)
this.setState({data: [1,2,3]}, () => {
console.log('здесь, уже точно state будет обновлен')
})Так же, чтобы обновить состояние, основываясь на предыдущее, подойдет следующая запись:
this.setState((prevState, props) => {
return {counter: prevState.counter + props.step};
});>> Зачем многие постоянно пишут в constructor: this.FUNCTION_NAME = this.FUNCTION_NAME.bind(this) и отсюда вопрос вытекает чему равно this в разных местах вашего компонента ?
“Биндинг” чего-либо, это привязка к контексту. То есть, когда вы находитесь в конструкторе, this ссылается на компонент. Следовательно, если какой-то из методов “прибиндить” (привязать) к this, то он всегда будет иметь внутри себя this ссылающийся на компонент. Так делается, в частности из-за того, чтобы не терять контекст this, когда идет запись не через arrow функцию.
>> В каких методах жизненного цикла стоит выполнять xhr запросы и почему?
xhr (ajax, асинхронные запросы) — нужно выполнять в момент componentDidMount
Метод componentDidMount вызывается в том случае когда закончен рендер в DOM и с этого момента можно менять state.
В этом методе так же можно ставить обработчики событий!
>> Зачем нужен componenWillUnmount ? Приведите пример
componenWIllUnmount подходит для удаления обработчиков событий которые были установлены в методе componentDidMount .
>> Зачем нужен componenWillMount ?
В этом методе можно записать значения из props в state
Deprecated in 17 version
>> Что такое state ?
State это локальное состояние обьекта. Если в методе render() выводятся значения одного из состояния, то при смене состояния компонент будет перерисовываться.
State можно поменять только через метод setState(). Замена состояний будет не мгновенно так как это асинхронный метод. Напрямую состояния менять запрещено!
>> Что такое props?
Props это те начальные параметры которые передаются компоненту извне (компонентов-родителей).
<List items={this.state.items} />Исходя из примера items и будет props для компонента List
Props не можно менять напрямую. Для смены необходимо в props передать родительский метод который через родителя будет менять значения.
Пример родителя
class Parent extends Component {
state = {
items: []
}
constructor() {
this.addItem = this.addItem.bind(this);
} addItem(item) {
this.setState((prevState, props) => {
return {
items: prevState.items.concat(item);
}
});
} render() {
return (
<List
addItem={this.addItem}
items={this.state.items}
/>
)
}
}
В дочернем компоненте List достаточно вызвать метод this.props.addItem(elem) и передать новый елемент elem
Тогда состояния родительского компонента поменяются и после этого вызовется метод render()
>> Можно ли менять props напрямую ?
Как говорит документация props только для чтения!
>> Чем отличаются state от props ?
State это локальные состояния компонента которые можно поменять. Props это параметры компонента которые были заданы компонентом родителем. Props read-only!
При смене state или props вызывается метод render();
>> Зачем используются ключи в React и когда надо использовать ?
Keys (ключи) помогают React отследить какие элементы были изменены, добавлены или удалены из списка.
Важно, чтобы каждый ключ был уникальным между “собратьями”. Мы уже говорили о процессе согласования и в частности о процессе сравнения нового дерева элементов с предыдущим. Keys делают этот процесс более эффективным при работе со списками, потому что React может использовать ключ на дочерний элемент, чтобы быстро узнать если элемент является новым или если он был просто перемещен при сравнении деревьев элементов. Не только keys делают этот процесс более эффективным, но без них, React не сможет узнать какое локальное состояние соответствует какому пункту при его перемещении. Поэтому не пренебрегайте использованием keys при применении map.
Используются ключи в циклах и map.
>> Что такое Redux и зачем его используют ?
Redux это билбиотека которая управленяет состоянием (данными) всего вашего приложения.
>> Что такое действие (action)и создатель действия (action creator) ?
Действие (action) — это простой объект с обязательным полем type и не обязательным payload (либо любым другим).
Создатель действия (action creator) — это функция, которая возвращает действие.
Пример создателя действия:
function addToto() {
return {
type: ADD_TODO,
payload: 'Пройти собеседование',
}
}Пример действия (напоминаю, это простой объект):
{
type: ADD_TODO,
payload: 'Пройти собеседование',
}Что такое редьюсер?
Редьюсер, слово производное от функции reduce. Это такой элемент Redux, который принимает прошлое состояние и возвращает следующее.
>> Рассказать полный алгоритм работы Redux ?
- Компонент запрашивает действие. Создатель действия форматирует и возвращает его.
- Через метод dispatch действие передается на хранилище.
- Хранилище получает действие. Оно посылает текущее дерево состояния и само действие корневому редюсеру
- Корневой редюсер разделяет дерево состояния на части. Затем он передаёт каждый кусочек в подчинённый редюсер, который в курсе, что с ним делать.
- Подчинённый редюсер копирует эту часть и вносит изменения в копию. Он возвращает копию корневому редюсеру.
- Как только все подчинённые редюсеры вернут копии составных частей, корневой редюсер склеит их вместе, чтобы сформировать целиком обновлённое дерево состояния, которое и будет возвращено хранилищу. Хранилище заменит старое дерево новым.
- Хранилище говорит связыванию уровня представления, что появилось новое состояние.
- Связывание уровня представления просит хранилище переслать новое состояние.
- Связывание уровня представления вызывает перерисовку.

>> Что такое Middleware ?
Middleware это усилитель. Усиливает хранилище функционалом. Принимает входящие данные, добавляет функционал (еще одни данные) и дальше передает.
Пример
Redux-thunk нужен для того, чтобы внутри создателя действия у вас была функция dispatch, чтобы с помощью нее “диспатчить” другие действия. Код redux-thunk’a состоит из 14 строк.
>> Когда и где надо использовать middleware?
Middleware это предлагаемый способ расширения Redux с помощью настраиваемых функций. Mидлвар позволяет вам обернуть метод хранилища dispatch для пользы и дела. Ключевой особенностью мидлвара является то, что они компонуемы. Несколько мидлваров можно объединить вместе, где каждый мидлвар не должен знать, что происходит до или после него в цепочке.
Наиболее распространенным случаем использования мидлваров является поддержка асинхронных действий без большого количества шаблонного кода или зависимости от библиотек типа Rx. Это позволяет вам вызывать асинхронные действия помимо обычных действий.
Например, redux-thunk позволяет генераторам действий инвертировать управление вызывая функции. Они будут получать dispatch как аргумент и могут вызывать его асинхронно. Такие функции называются преобразователями (thunks). Другим примером мидлвара является redux-promise. Он позволяет вам вызывать асинхронное действие c Promise и вызывать обычные действия, когда промис вернет resolve.
Mидлвары нельзя сравнивать с createStore и это не фундаментальная часть архитектуры Redux, но, мы считаем, что достаточно полезно поддерживать их прямо в ядре. Таким образом, существует единственный стандартный способ расширить dispatch в экосистеме и разные мидлвары могут конкурировать в выразительности и полезности.
>> Что такое сборщики? Какие существуют?
Сборщики собирают ресурсы и формируют из них бандлы.
Webpack, Gulp, Grunt
>> Чем отличается merge от rebase ?
rebase сливает ветки не сохраняет историю коммитов, все локальные коммиты будут сверху. Коммит при этом действии не делается!
rebase рекомендуется использовать для своих локальных веток, только тех с которыми работаете только вы.
merge сливает ветки в отдельном коммите который называется “Merge …”. Сохраняется история комитов в том порядке который был во время разработки (соблюдает хронологичный порядок коммитов других пользователей и Ваших коммитов)
>> Рассказать о GitFlow?
1. Создается репозиторий
2. Репозиторий инициализируется
3. Начинается работа на ветке develop
4. Возникает необходимость опробовать новую штуку — создается feature-ветка и делаются коммиты
5. Закончив работу на feature-ветке, вы сливаете ее с develop
6. Если вы довольны текущей версией, но хотите продолжить работу, создается ветка release от develop, куда перемещается текущая версия. Правка багов будет происходить на этой же ветке.
7. Когда с веткой release покончено, время слить ее в master и в develop и продолжить работу с develop
8. При возникновении проблем от ветки master создается ветка hotfix. Там же быстро устраняются нежелательное состояние продакшн-версии продукта. Завершение исправлений происходит слитием ветки в master и develop.

>> Провести код ревью ?
class Test extends Component {
componentWillMount() {
this.refs.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.refs.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState({
clicks: this.clicks + i
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}Ошибки
Вместо componentWillMount должен быть componentDidMount так как обработчик можно добавить только к отрендеренному компоненту.
class Test extends Component {
componentDidMount() {
this.refs.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.refs.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState({
clicks: this.clicks + i
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}В этом же методе вместо this.refs.myComponent должно быть this.myComponent.addEventListener(‘click’, this.onclick)
class Test extends Component {
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.refs.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState({
clicks: this.clicks + i
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}Обработчик this.onclick ничего не знает о контексте по этому добавляем конструктор и биндим контекст.
class Test extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.refs.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState({
clicks: this.clicks + i
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}В методе componentWillUnmount обращаемся по reference
class Test extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState({
clicks: this.clicks + i
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}В методе setState() допущена ошибка. Во первых clicks надо брать из состояния. Метод setState() дает возможность получить предыдущие состояния.
class Test extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState((prevState) => {
return {
clicks: prevState.clicks + i
}
})
}
render() {
return (
<div id="MyComponent" ref="myComponent">
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}В render() методе в елемента с id “MyComponent” неверно получаем ссылку на DOM елемент. Правильно указывать через коллбэк
class Test extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState((prevState) => {
return {
clicks: prevState.clicks + i
}
})
}
render() {
return (
<div id="MyComponent" ref={elem => this.myComponent = elem}>
<p> Clicks ({this.clicks}} clicks)</p>
</div>
)
}
}Так же в render() методе ошибка в выводе текущего состояния, лишняя фигурная скобка и пара круглых скобок
class Test extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
this.myComponent.addEventListener('click', this.onclick)
}
componentWillUnmount() { this.myComponent.removeEventListener('click', this.onclick)
}
onClick() {
this.setState((prevState) => {
return {
clicks: prevState.clicks + i
}
})
}
render() {
return (
<div id="MyComponent" ref={elem => this.myComponent = elem}>
<p> Clicks {this.state.clicks} clicks</p>
</div>
)
}
}>> Написать реализацию компонента 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 {
// закончите код здесь
}
Props для компонента будет username и children (render callback)
Так как есть асинхронный елемент (промис) то нам нужен метод componentDidMount() и state, так как надо записывать результат выполнения промиса.
Если внимательно посмотреть то в рендер коллбэке есть тернарная операция сравнения с null ((user) => user === null ? … :…), значит начальное остояние будет null.
class Twitter extends Component {
state = {
user: null
}
componentDidMount() {
fetchUser(this.props.username)
.then(user => {
this.setState({user});
});
}
}Добавляем рендер коллбэк
class Twitter extends Component {
state = {
user: null
}
componentDidMount() {
fetchUser(this.props.username)
.then(user => {
this.setState({user});
});
} render() {
return this.props.children(this.state.user);
}
}
Так как в задании еще упоминались PropTypes можно описать username как string и required (так как без этого параметра будет ошибка)
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);
}
}
