Неназванная канадская платформа электронной коммерции (да, та) искала стажеров, и я именно тот стажер.

Их процесс подачи заявок не был похож ни на один из тех, с которыми я сталкивался раньше. Мне было поручено создать веб-приложение, которое использует API фильмов OMDB (бесплатно для данных фильмов и имеет платную возможность получать постеры к фильмам. Проверьте это на Patreon) , отображает результаты поиска и имеет возможность добавлять и удалять номинации к любому показанному заголовку с ожидаемой функциональностью для кнопок (или div), которые будут выполнять эти задачи. Эта сборка должна была быть завершена и отправлена ​​вместе с заявкой на рассмотрение.

В этом посте я сосредоточен на своем образе мышления и некоторых проблемах, с которыми я столкнулся при создании этого поискового приложения, а также на будущих целях по его полировке в качестве проекта в моем портфолио. Вы можете увидеть мой репозиторий приложения на моем Github здесь: Frontend, Backend. Мой код будет периодически обновляться, и код в статье актуален по состоянию на 5.11.21 и находится в том состоянии, в котором он был отправлен для работы.

Первое, что я хочу затронуть в связи с сборкой, - это разговор, который у меня был с другом на тему:

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

Я бился над этим вопросом в течение нескольких дней, прежде чем начать, и высказанные опасения, на мой взгляд, были обоснованными. В конце концов я пришел к выводу, что да, оно того стоит. Даже если я не получу эту должность, у меня есть новый проект для моего портфолио, и я получаю много практики, работая с выбранными мной технологиями так, как раньше, что подтвердилось, оглядываясь назад. Я смог укрепить свое понимание потока данных между React и Redux + thunk в бэкэнд Rails, в базу данных и обратно во фронтенд. Я также смог немного попрактиковаться с CSS (который я люблю использовать, но сейчас это одно из моих самых слабых мест) и медиа-запросами.

Что касается сборки, я решил использовать React и Redux в качестве интерфейса, а Ruby on Rails - в качестве бэкенда. Изначально у меня все было структурировано как интерфейс только с любыми данными, которые должны сохраняться в компонентах, обрабатываемых с помощью состояния приложения. Примерно на полпути это оказалось нереальным и не совсем то, что я хотел. Мои компоненты внешнего интерфейса имеют довольно простую структуру: компонент Search, компонент SearchResults, привязанный к состоянию redux, и компонент Signup & Login + Loading, привязанный к их собственному состоянию. Все эти компоненты вложены в React Router и имеют свой собственный маршрут. Я чувствовал, что эта простая установка позволяет выполнять поставленные передо мной задачи достаточно эффективно, чтобы удовлетворить требования.

В этом посте я хочу разобрать компоненты Search и SearchResults и то, как они были построены. Во-первых, мне нужно поговорить об API, который я должен использовать, и о том, как именно мне удалось сохранить ключ API в безопасности. Этого легко добиться, но в первый раз немного сложно. Я создал файл с именем .env в корне своей папки внешнего интерфейса. В этом файле я создал переменную с именем REACT_APP_API_KEY и установил для нее ключ, сгенерированный на сайте API. По понятным причинам я не буду здесь изображать эту строчку кода, но на самом деле она настолько проста, насколько кажется. Самым важным шагом здесь, без сомнения, является добавление .env файла в ваш .gitignore файл. Если вы этого не сделаете, ключ API будет загружен на GitHub, чтобы его могли увидеть все любопытные.

Для этого конкретного API это еще не конец света; если это произойдет, скажем, ваша учетная запись AWS, с вашей кредитной карты может быть снята довольно большая сумма, которую вы не ожидали (не говоря уже о любой потере данных, которая могла происходить). Крайне важно сделать все возможное, чтобы защитить свои ключи API.

Наконец, после правильного сохранения ключа API мне нужен способ доступа к нему. Я сделал это внутри index.js. Я создал переменную для экспорта с простым названием API, установил ее равной URL-адресу API и использовал интерполяцию строк, чтобы добавить свой ключ к URL-адресу. Это легко достигается с помощью объекта process внутри React и свойства .env, в котором находится мой REACT_APP_API_KEY.

Следующим шагом в доступе к данным в API является фактический вызов запроса на выборку с помощью React.

Для краткости я не буду объяснять весь поток данных между всеми моими файлами, а буду рисовать для них код. Для объяснения потока данных в React / Redux ознакомьтесь с этой статьей. Я также сопоставил состояние отправки и приложения с характеристиками одного из моих компонентов. Подробнее об этом смотрите здесь. Это важные концепции, но они не являются основной темой моего поста, поэтому я продолжу предполагать базовое понимание этих концепций.

Запрос на выборку изначально запускается из моей панели поиска.

Простой тег input с функцией onChange; каждый раз, когда набирается символ, вызывается функция, которая запускает функцию, которая (на данный момент) просто вызывает действие, сопоставленное с реквизитами компонентов, называемыми exSearch.

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

Файл exSearch.js импортирует мою переменную API, определенную ранее, и использует ее внутри запроса на выборку. В документации OMDB указано, что для поиска в API нам нужно добавить &s= к URL-адресу после ключа и заголовок для поиска. Это было довольно просто при разработке с использованием строковой интерполяции. Во втором операторе .then мне нужно было сначала проверить, был ли ответ от API фактическим массивом заголовков (во время первых трех или четырех вариантов символов он отвечает, что совпадений слишком много для отображения), и что искомый заголовок был действительным. Это последнее условие внутри моего действия было специально для устранения проблемы, из-за которой мое приложение ломалось после ввода заголовка и удаления всех символов. Последний удаленный символ отправит запрос на заголовок ‘’ и, глубже по пути данных, попытается отобразить пустую строку, что вызовет ошибку. В зависимости от выполненного условия действие отправит отправку в мой магазин для обновления состояния.

Для простоты я решил использовать в приложении только один редуктор. Внутри этого редуктора фактически установлено мое состояние. Что касается состояния, приложение очень простое, всего с четырьмя свойствами; currentUser: строка id, используемая для внутренних запросов, searchTitles: строка, в которой выполняется поиск, currentUserTitles: массив объектов со всеми заголовками, связанными с текущим пользователем, и allNominatedTitles: массив объектов со всеми заголовками, назначенными для всех пользователей. Случай UPDATE_SEARCH редуктора краток, просто устанавливает свойство searchTitles в состоянии равным desiredTitle внутри функции exSearch.

Настоящая магия начинается внутри компонента SearchResults, вложенного в компонент Search. Здесь у меня есть целый беспорядок, переданный из Search, наиболее важным из которых является titles (то есть this.props.searchTitles), которые являются searchTitles, которые мы обновили выше.

В моей функции render of SearchResults у меня есть однострочный троичный оператор, который проверяет searchTitles в состоянии.

Если заголовка нет (т.е. поиск не производился), запускается функция rollNominated, которая возвращает все номинированные заголовки. Если есть заголовки и был выполнен поиск, функция showSearch вернет заголовки из API, соответствующие вводимым.

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

Вы заметите, что в обеих этих функциях есть const с именем source. Этот const присутствует и передается во все функции, которые имеют дело как с состоянием, будь то allNominatedTitles или currentUserTitles, так и обрабатывают любые возвраты непосредственно из API. Это необходимо для работы приложения в его текущей сборке, потому что отдельные объекты, возвращаемые из API (по какой-то причине), структурированы заглавными буквами для имен свойств, а моя база данных структурирована строчными буквами ( знаешь, как нормальный человек). Это сделано таким образом, потому что ошибки, с которыми я столкнулся в конце сборки, возникали несколько раз, а не другие, и к тому времени, когда я понял, как исправить эти проблемы, у меня оставалось только около 6 часов, и под рукой было больше насущных проблем, чем реструктуризация миграции моей базы данных и исходных файлов. Ниже я изобразил функции, которые используют эти исходные переменные.

Условия, основанные на этих исходных переменных, устанавливаются, потому что свойства currentUserTitles и allNominatedTitles состояния извлекаются из запросов на выборку, отправленных на мой сервер. Затем запись в базе данных создается каждый раз, когда номинируется титул. Каждое название имеет только одну запись для себя и соответствующие записи для каждой полученной номинации с простыми user_id и title_id в качестве данных. Пользователь может назначить титул только один раз.

Мне сложно понять, как все это закончить, поэтому спасибо, что пришли на мой ted talk.

Если вы хотите проверить приложение на себе, вперед. Мне известно о нескольких ошибках, которые я собираюсь исправить, но если вы обнаружите их сами и захотите рассказать мне об этом, не стесняйтесь отвечать прямо на это сообщение или напишите мне об этом по адресу jvckmo @ gmail.com.

P.S.

Неназванная канадская платформа электронной коммерции (да, именно та), пожалуйста, наймите меня.