IT-инфраструктура для бизнеса и творчества
Разработка
Max Patsitansky

Тестовое задание и code-review для новичков (frontend, react)

Привет будущим frontend-разработчикам (и некоторым бывалым).

Мы (я и мой коллега Роман) проводим тестовое задание с разбором ваших ошибок. Сегодня мы анонсируем повторный запуск первого ТЗ (видео с прошлого разбора, но если вы планируете выполнять - не торопитесь смотреть).

Ключевой игрок - React. Задание подходит новичкам, не подходит опытным разработчикам [1].

[1] - для тех, кто с легкостью пишет на JS и React, предлагаем выполнить данное ТЗ на TypeScript и Reach router. А также вы можете дополнить выполнение Docker-образом. Получите новый опыт.

Участие бесплатное.

Что проверяем?

Начальные знания по React, Redux и роутингу. Текст задания.

Примерный результат

Порядок проведения

Вы выполняете задание и присылаете письмо со ссылкой на гитхаб. На разборе обсуждаются как коллективные ошибки, так и индивидуальные. Так же обсуждаются хорошие решения и выбираются топ-3 работы.

Плюсы:

  • вам указывают на ошибку
  • вам подсказывают решение
  • вы смотрите как эту же проблему решали другие участники

Минусы:

  • публичное порицание за халтуру (не путать с “непреднамеренными ошибками”)

Рекомендации к выполнению

Заполните README .md по образцу

После того как сделали, посмотрите подобный разбор (видео), исправьте ошибки.

Для тех, кто пишет на TS - используйте строгие правила:

  • noImplicitAny
  • noImplicitThis
  • alwaysStrict
  • strictNullChecks
  • strictFunctionTypes
  • strictPropertyInitialization

Подробнее здесь (EN).

Предоставьте ссылку на demo и добавьте оформление проекту.

Дедлайн

Прием работ заканчивается ровно через месяц: 11 апреля 2019 года. Не тормозите, изучение без практики невозможно.

Работы присылайте мне на почту (maxpfrontend@gmail.com) с темой письма: TZ #1 v2.0

Если есть вопросы - задавайте в комментариях.

{ "author_name": "Max Patsitansky", "author_type": "self", "tags": ["1"], "comments": 8, "likes": 13, "favorites": 14, "is_advertisement": false, "subsite_label": "dev", "id": 60753, "is_wide": true, "is_ugc": true, "date": "Mon, 11 Mar 2019 12:37:33 +0300", "is_special": false }
(function () { let cdnUrl = `https://specialsf378ef5-a.akamaihd.net/SelectelBranding/images/` let previousArticleNumber = null let currentArticleNumber = 0 let platform = 'Desktop' let articles = [ // { // name: 'camera', // url: `${cdnUrl}CameraCat`, // text: 'умную камеру для\u00A0наблюдения за\u00A0котиками', // link: '1', // num: 3 // }, { name: 'chill', url: `${cdnUrl}ChillCat`, text: 'трекер, который подскажет, когда пора отдохнуть', link: 'https://vc.ru/promo/288561-eye-tracker', num: 1 }, { name: 'cloud', url: `${cdnUrl}CloudCat`, text: 'котика: даёшь ему «пять», а\u00A0он делает бэкап в облако', link: 'https://vc.ru/dev/294799-maneki-neko', num: 2 } ] let buttonCycle = document.querySelector('.button--cycle') let buttonChoose = document.querySelector('.button--choose') let buttonMobile = document.querySelector('.button--mobile') let textField = document.querySelector('.selectel-footer-subtitle') let imageAgent = document.querySelector('.image--agent') let banner = document.querySelector('.selectel-footer') buttonCycle.addEventListener('click', cycleClick) buttonChoose.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) buttonMobile.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) let media = window.matchMedia("(max-width: 570px)") media.addEventListener('change', matchMedia) function matchMedia() { if (media.matches) { platform = 'Mobile' } else { platform = 'Desktop' } update() } matchMedia() function cycleClick(event) { sendEvent(`Promo ${articles[currentArticleNumber].num} Right`, 'Click') if (event) { event.preventDefault() event.stopPropagation() } window.open('https://vc.ru/tag/selectelDIY', '_blank') //cycle(event) } function cycle(event) { // incrementArticleNumber() textField.innerHTML = generatedText() imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?3' imageAgent.setAttribute("class", "") imageAgent.classList.add('image--agent', articles[currentArticleNumber].name) banner.href = articles[currentArticleNumber].link } function update() { banner.href = articles[currentArticleNumber].link imageAgent.src = articles[currentArticleNumber].url + platform + '.svg' textField.innerHTML = generatedText() } function incrementArticleNumber() { previousArticleNumber = currentArticleNumber if (currentArticleNumber >= articles.length - 1) { currentArticleNumber = 0 } else { currentArticleNumber++ } } const sendEvent = (label, action = 'Click') => { const value = `SelectelDIY — loc: Footer — ${label} — ${action}`; if (window.dataLayer !== undefined) { window.dataLayer.push({ event: 'data_event', data_description: value, }); } }; function generatedText() { let defaultText if (platform === 'Desktop') { defaultText = `Мы тут собрали %text%. Хотите научим?` } else { defaultText = `Мы тут собрали %text%.` } return defaultText.replace('%text%', articles[currentArticleNumber].text) } function getRandom(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min } (function create() { currentArticleNumber = getRandom(0, articles.length - 1) cycle() let page = document.querySelector('.page--entry') if (page) { function insertAfter() { let parents = page.querySelectorAll('[data-id="7"]') let referenceNode = parents[0] referenceNode.parentNode.insertBefore(banner, referenceNode.nextSibling); loaded() } setTimeout(() => insertAfter(), 0) } }()) function loaded() { banner.classList.add('loaded') } loadImages([ `${cdnUrl}CameraCatDesktop.svg`, `${cdnUrl}ChillCatDesktop.svg`, `${cdnUrl}CloudCatDesktop.svg`, `${cdnUrl}CameraCatMobile.svg`, `${cdnUrl}ChillCatMobile.svg`, `${cdnUrl}CloudCatMobile.svg?3`, ]) function loadImages(urls) { return Promise.all(urls.map(function (url) { return new Promise(function (resolve) { var img = document.createElement('img'); img.onload = resolve; img.onerror = resolve; img.src = url; }); })); } }())
0
8 комментариев
Популярные
По порядку
Написать комментарий...

А можно сделать авторизацию пользователя через firebase?

0

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

0

Прочитал публикацию на habr, здесь уже все моменты разобраны, в т. ч. с TS и hooks. Что тогда еще остается сделать? ред.

0

Остается добавить Redux и переписать логику с хуков, на redux там, где нужно.

0

Привет, при вызове внутри useEffect экшена который диспатчит в store массив новостей, при первой отрисовке страницы эффект вызывается 2 раза, в depends передаю ему длину массива, подскажи как избавиться от этого перерендера для новостей. https://github.com/AlekseyPn/react_study_app/tree/redux компонент News.

0

Или нам не нужно в хуках этого делать в задании а просто прикрутить redux через statefull компоненты?

0

Я думаю новости надо забирать redux-ом, процитирую:
While hooks are local, some state is global. And that means that state managers will always be your friend! (в переводе: пока хуки локальны, кое-что в стейте глобально. И это значит, что стейт-менеджер (Redux/mobx/etc..) все еще ваш друг)

Взял отсюда: https://frontarm.com/james-k-nelson/react-hooks-intuition/

0

Спасибо, просто хотел попробовать все это подружить, но дружится кривовато :) меня не устраивает такой вариант, спасибо за инфу и пояснения :)

0
Читать все 8 комментариев
Брендинг в fintech late 2021: от милых иллюстраций до премиальных 3D

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

Aviasales запустил свой первый платный сервис — с кэшбеком у партнёров, секретными маршрутами и приоритетной поддержкой Статьи редакции

На старте «Ещё» стоит 990 рублей в год.

Объявлены победители Finlanding
«Spotify: История продукта». Как мы разработали алгоритмы музыкальных рекомендаций

Из онлайн-библиотеки — в сервис персонализированных рекомендаций.

eBay выбрал лучших российских экспортеров 2021 года

Согласно статистике eBay, за период с марта по октябрь 2021 года число российских экспортеров на платформе увеличилось на 19% и подошло к 50 000. Самой быстрорастущей категорией на маркетплейсе в России стала «Дом и сад» — экспорт этих товаров на eBay вырос в 16 раз по сравнению с прошлым годом. Кроме того, продажи «Спортивной атрибутики с…

Как я сделал за неделю бесплатный сервис приема чаевых SpasiboZa

Как-то раз, сидя на стрижке у своего барбера, задался вопросом: как было бы здорово отправить ему чаевые, отсканировав QR-код на зеркале, как в ресторанах. Так появился сервис spasiboza.ru

Онлайн-ритейлеры в России сообщили о дефиците курьеров и сборщиков заказов Статьи редакции

Ситуация усугубится в праздники, из-за этого стоимость и время доставки вырастут, считают аналитики.

Кошелек скажет спасибо: зачем банки заводят подкасты, а люди их слушают

Подкастинг – очень молодой (сам термин вошел в оборот в 2004 году с подачи британского журналиста), но стремительно развивающийся рынок. Его перспективы быстро оценили и в банках.

Шпаргалка для инвестора: сделки с неполным покрытием

Рассказываем инвесторам-новичкам о возможностях и нюансах необеспеченных сделок.

Озонатор опасен для лёгких, а кондиционер не освежает: как разобраться в «гирлянде» гаджетов для воздуха, не теряя денег

И на каких бесполезных устройствах можно сэкономить.

Бризер с увлажнителем и очистителем может решить сразу три проблемы с воздухом. Источник
null