├── 01.extend-extends.jsx ├── 02.no-super.jsx ├── 03.async-state.jsx ├── 04.jsx-syntax.jsx ├── 05-event-handler-call.jsx ├── 06-event-bind.jsx ├── 07-event-capture.jsx ├── 08-event-bind-why.jsx ├── 09.wrong-state-write.jsx ├── 10.wrong-ref.jsx ├── README.md └── templates ├── 01.button-count.jsx ├── 02.button-count-redhot.jsx ├── 03.form-dom-validity.jsx ├── 04.form-validity.jsx └── README.md /01.extend-extends.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: почему не работает код 2 | 3 | class Button extend React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | clicked: 0 8 | }; 9 | } 10 | 11 | render() { 12 | return ; 13 | } 14 | 15 | onClick() { 16 | this.setState({ 17 | clicked: this.state.clicked + 1 18 | }); 19 | } 20 | } 21 | 22 | ReactDOM.render(; 12 | } 13 | 14 | onClick() { 15 | this.setState({ 16 | clicked: this.state.clicked + 1 17 | }); 18 | } 19 | } 20 | 21 | ReactDOM.render(; 16 | } 17 | 18 | onClick() { 19 | this.setState({ 20 | clicked: this.state.clicked + 1 21 | }); 22 | 23 | if (this.state.clicked >= 10) { 24 | this.setState({ 25 | redHot: true, 26 | }); 27 | } 28 | } 29 | } 30 | 31 | ReactDOM.render(; 16 | } 17 | 18 | onClick() { 19 | this.setState({ 20 | clicked: this.state.clicked + 1 21 | }, () => { 22 | this.setState({ 23 | redHot: this.state.clicked >= 10, 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | ReactDOM.render( 18 | ; 19 | } 20 | 21 | onInput() { 22 | this.setState({ errorMessage: '' }); 23 | } 24 | 25 | onInvalid(evt) { 26 | evt.preventDefault(); 27 | this.setState({ 28 | errorMessage: evt.target.name === 'fullname' 29 | ? 'Введите имя, это обязательное поле' 30 | : 'Введите возраст. Возраст не должен быть меньше 18 лет' 31 | }); 32 | } 33 | } 34 | 35 | ReactDOM.render(
, document.querySelector("#app")); 36 | 37 | // Варианты ответа 38 | // 1. Обработчики не добавляются, а вызываются, нужно убрать круглые скобки [правильный ответ] 39 | // 2. С этим кодом всё хорошо 40 | // 3. В onInput и onInvalid нужно передавать this.props, а не this, так работает жизненный цикл компонент 41 | // 4. Неправильная проверка пустого состояния errorMessage в методе render. Чтобы этот код работал, errorMessage должен быть равен null 42 | -------------------------------------------------------------------------------- /06-event-bind.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: Что не так с этим кодом? 2 | 3 | class Form extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | errorMessage: '', 8 | }; 9 | } 10 | 11 | render() { 12 | return 13 | {this.state.errorMessage ?
{this.state.errorMessage}
: null} 14 | 15 | Ваше имя:
16 | Ваш возраст:
17 | 18 |
; 19 | } 20 | 21 | onInput() { 22 | this.setState({ errorMessage: '' }); 23 | } 24 | 25 | onInvalid(evt) { 26 | evt.preventDefault(); 27 | this.setState({ 28 | errorMessage: evt.target.name === 'fullname' 29 | ? 'Введите имя, это обязательное поле' 30 | : 'Введите возраст. Возраст не должен быть меньше 18 лет' 31 | }); 32 | } 33 | } 34 | 35 | ReactDOM.render(
, document.querySelector("#app")); 36 | 37 | // Варианты ответа 38 | // 1. Код обработчиков выполняется с неправильным контекстом [правильный ответ] 39 | // [Контекст выполнения обработчиков всегда равен элементу на котором произошло 40 | // событие, поэтому, нужно исправить код так, чтобы this в обработчиках 41 | // ссылался на реакт компоненту, а не на DOM-ноды. Для этого надо или сделать 42 | // bind для обработчиков или вызвать обработчики внутри стрелочной функции] 43 | // 2. Обработчики onInput и onInvalid нельзя ставить на форму, они должны быть 44 | // на каждом элементе отдельно 45 | // 3. Форма не отрендерится, ей обязательно нужно указать action 46 | // 4. Атрибуты min и step для возраста нужно указывать строкой 47 | -------------------------------------------------------------------------------- /07-event-capture.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: В этом коде не работает обработка события invalid, как починить? 2 | 3 | class Form extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | errorMessage: '', 8 | }; 9 | } 10 | 11 | render() { 12 | return 13 | {this.state.errorMessage ?
{this.state.errorMessage}
: null} 14 | 15 | Ваше имя:
16 | Ваш возраст:
17 | 18 |
; 19 | } 20 | 21 | onInput() { 22 | this.setState({ errorMessage: '' }); 23 | } 24 | 25 | onInvalid(evt) { 26 | evt.preventDefault(); 27 | this.setState({ 28 | errorMessage: evt.target.name === 'fullname' 29 | ? 'Введите имя, это обязательное поле' 30 | : 'Введите возраст. Возраст не должен быть меньше 18 лет' 31 | }); 32 | } 33 | } 34 | 35 | ReactDOM.render(
, document.querySelector("#app")); 36 | 37 | // Варианты ответа 38 | // 1. Вместо onInvalid нужно написать onInvalidCapture [правильный ответ] 39 | // [Обработка не работает, потому что у этого события нет фазы всплытия, 40 | // поэтому его нужно ловить на фазе захвата.] 41 | // 2. onInvalid нужно убрать с формы и поставить на инпуты [правильный ответ] 42 | // [Обработка не работает, потому что у этого события нет фазы всплытия, 43 | // поэтому можно поставить обработчики на каждый из элементов формы и забить 44 | // на делегирование. Это будет работать, но это небольшой оверхед] 45 | // 3. Такого события не существует, нужно обрабатывать событие submit 46 | // 4. Правильное название события не onInvalid а onIncorrectInput 47 | -------------------------------------------------------------------------------- /08-event-bind-why.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: Ок, поставили bind для обработчиков и сделали capture. Как можно оптимизировать создание 2 | // обработчиков? 3 | 4 | class Form extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | errorMessage: '', 9 | }; 10 | } 11 | 12 | render() { 13 | return 14 | {this.state.errorMessage ?
{this.state.errorMessage}
: null} 15 | 16 | Ваше имя:
17 | Ваш возраст:
18 | 19 |
; 20 | } 21 | 22 | onInput() { 23 | this.setState({ errorMessage: '' }); 24 | } 25 | 26 | onInvalid(evt) { 27 | evt.preventDefault(); 28 | this.setState({ 29 | errorMessage: evt.target.name === 'fullname' 30 | ? 'Введите имя, это обязательное поле' 31 | : 'Введите возраст. Возраст не должен быть меньше 18 лет' 32 | }); 33 | } 34 | } 35 | 36 | ReactDOM.render(
, document.querySelector("#app")); 37 | 38 | // Варианты ответа 39 | // 1. Перенести создание .bind-версий обработчиков в конструктор [правильный ответ] 40 | // [В текущей версии кода при каждом вызове render будет создаваться новый 41 | // обработчик, который будет представлять собой функцию, с привязанным контекстом. 42 | // Чтобы избежать создания абсолютно одинаковых функций, при каждом вызове render, 43 | // можно создать функции один раз и они будут переиспользоваться] 44 | // 2. Добавить evt.preventDefault() в onInput() 45 | // 3. Наоборот, убрать evt.preventDefault() из onInvalid() 46 | // 4. Нужно в оба обработчика добавить evt.stopPropagation() 47 | -------------------------------------------------------------------------------- /09.wrong-state-write.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: Почему эта форма не заработает? 2 | 3 | class Form extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | this.state = { 8 | errorMessage: [], 9 | }; 10 | } 11 | 12 | render() { 13 | return 14 | Ваше имя:
15 | Ваш возраст:
16 | {this.state.errorMessage 17 | ?
{this.state.errorMessage}
18 | : null} 19 | 20 |
; 21 | } 22 | 23 | onInput() { 24 | if (!this.refs.formElementRef['fullname'].value) { 25 | this.state.messages.push('Введите имя, это обязательное поле'); 26 | } 27 | 28 | if (!this.refs.formElementRef['age'] || 29 | this.refs.formElementRef['age'].value < 18) { 30 | this.state.messages.push('Введите возраст. Возраст не должен быть меньше 18 лет'); 31 | } 32 | } 33 | } 34 | 35 | ReactDOM.render(
, document.querySelector("#app")); 36 | 37 | // Варианты ответа 38 | // 1. Идет неправильная запись в state в onInput [правильный ответ][state нужно 39 | // обновлять через setState,а не через прямое обращение к свойствам объекта] 40 | // 2. disabled у кнопки не будет переключаться, выражение закешируется 41 | // и не будет обновляться, даже когда изменится length массива, потому что 42 | // объекты передаются по ссылке 43 | // 3. Всё дело в тернарном операторе в render(), так код писать нельзя: нужно 44 | // использовать if 45 | // 4. В onInput неправильная проверка поля возраста 46 | -------------------------------------------------------------------------------- /10.wrong-ref.jsx: -------------------------------------------------------------------------------- 1 | // Вопрос: Почему эта форма не заработает? 2 | 3 | class Form extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | this.state = { 8 | errorMessage: [], 9 | }; 10 | } 11 | 12 | render() { 13 | return 14 | Ваше имя:
15 | Ваш возраст:
16 | {this.state.errorMessage 17 | ?
{this.state.errorMessage}
18 | : null} 19 | 20 |
; 21 | } 22 | 23 | onInput() { 24 | const messages = []; 25 | 26 | if (!this.refs.formElementRef['fullname'].value) { 27 | messages.push('Введите имя, это обязательное поле'); 28 | } 29 | 30 | if (!this.refs.formElementRef['age'] || 31 | this.refs.formElementRef['age'].value < 18) { 32 | messages.push('Введите возраст. Возраст не должен быть меньше 18 лет'); 33 | } 34 | 35 | this.setState({ 36 | errorMessage: messages, 37 | }); 38 | } 39 | } 40 | 41 | ReactDOM.render(
, document.querySelector("#app")); 42 | 43 | // Варианты ответа 44 | // 1. Всё нормально [правильный ответ][сложность не в использовании рефа, 45 | // а в обращении к элементу формы по name через нотацию с квадратными 46 | // скобками] 47 | // 2. При обращении к рефу нужно использовать `current`, 48 | // не `this.formElementRef['age']`, a `this.formElementRef.current['age']` 49 | // [Необязательно, тут создаётся реф как просто строка] 50 | // 3. Чтобы заработало нужно создать два отдельных рефа для каждого из инпутов 51 | // 4. disabled у кнопки не будет переключаться, выражение закешируется 52 | // и не будет обновляться, даже когда изменится length массива, потому что 53 | // объекты передаются по ссылке 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Задачи на собеседование на понимание React и JS в целом. В части задач всё написано правильно, но код не работает из-за синтаксических ошибок или мелких косяков из-за непонимания синтаксиса JS. В части задач всё работает и нужно это опознать. Этому может мешать, например, непривычное использование каких-нибудь API. 2 | 3 | Work in progress. 4 | -------------------------------------------------------------------------------- /templates/01.button-count.jsx: -------------------------------------------------------------------------------- 1 | // Базовая разметка: кнопка считает количество нажатий 2 | class Button extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | this.state = { 6 | clicked: 0 7 | }; 8 | } 9 | 10 | render() { 11 | return ; 12 | } 13 | 14 | onClick() { 15 | this.setState({ 16 | clicked: this.state.clicked + 1 17 | }); 18 | } 19 | } 20 | 21 | ReactDOM.render(; 16 | } 17 | 18 | onClick() { 19 | this.setState({ 20 | clicked: this.state.clicked + 1 21 | }, () => { 22 | this.setState({ 23 | redHot: this.state.clicked >= 10 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | ReactDOM.render( 16 |
; 17 | } 18 | 19 | onInput() { 20 | this.setState({ errorMessage: '' }); 21 | } 22 | 23 | onInvalid(evt) { 24 | evt.preventDefault(); 25 | this.setState({ 26 | errorMessage: evt.target.name === 'fullname' 27 | ? 'Введите имя, это обязательное поле' 28 | : 'Введите возраст. Возраст не должен быть меньше 18 лет' 29 | }); 30 | } 31 | } 32 | 33 | ReactDOM.render(
, document.querySelector("#app")); 34 | -------------------------------------------------------------------------------- /templates/04.form-validity.jsx: -------------------------------------------------------------------------------- 1 | class Form extends React.Component { 2 | constructor(props) { 3 | super(props); 4 | 5 | this.state = { 6 | errorMessage: [], 7 | }; 8 | } 9 | 10 | render() { 11 | return 12 | Ваше имя:
13 | Ваш возраст:
14 | {this.state.errorMessage 15 | ?
{this.state.errorMessage}
16 | : null} 17 | 18 |
; 19 | } 20 | 21 | onInput() { 22 | const messages = []; 23 | 24 | if (!this.refs.formElementRef['fullname'].value) { 25 | messages.push('Введите имя, это обязательное поле'); 26 | } 27 | 28 | if (!this.refs.formElementRef['age'] || 29 | this.refs.formElementRef['age'].value < 18) { 30 | messages.push('Введите возраст. Возраст не должен быть меньше 18 лет'); 31 | } 32 | 33 | this.setState({ 34 | errorMessage: messages, 35 | }); 36 | } 37 | } 38 | 39 | ReactDOM.render(
, document.querySelector("#app")); 40 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | Шаблонные файлы с правильным кодом компонент для разных задач. Есть шаблоны разных по сложности компонент 2 | 3 | - `01.button-count.jsx`: кнопка, считающая количество нажатий 4 | 5 | - `02.button-count-redhot.jsx`: кнопка, считающая количество нажатий и изменяющая цвет после достижения определённого количества 6 | 7 | - `03.form-dom-validity.jsx`: форма, валидирующаяся через DOM API. Элементам поставлены соответствующие атрибуты и при наступлении события invalid (попытка отправить форму), в специальный блок выводится сообщение об ошибке в соответствующем поле 8 | 9 | - `04.form-validity.jsx`: кастомная проверка валидности формы: при любом изменении поля, проверяется валидность всех полей формы и список ещё невалидных полей выводится над кнопкой отправки. Кнопка заблокирована, пока форма не будет отправлена 10 | --------------------------------------------------------------------------------