На сегодняшний день я думал, что мы можем создать приложение с помощью React, и поскольку люди из группы Chingu, в которой я работаю, работают над созданием приложения для поиска книг, я решил создать нечто подобное. Я также хотел избежать создания одного и того же приложения, поскольку я бы испортил их, написав эту статью. ^ _ ^
Итак ... Мы собираемся создать приложение для поиска фильмов! : D
Ниже вы можете увидеть дизайн карточки фильма, который мы собираемся использовать в нашем приложении:
Дизайн был вдохновлен этим Dribbble от Rizka Prayuda.
Примечание. В этой статье я не буду вдаваться в подробности того, как работает React (хотя мы будем использовать только базовые концепции). Если вы прочитаете дальше, я предполагаю, что вы уже работали с React раньше. Хотя бы чуть-чуть. ;)
Компонент Movie Card
Перед созданием компонента Movie Card нам нужно поговорить об API, который мы собираемся использовать, потому что он в основном будет определять, как будет выглядеть структура HTML.
В этом примере я использовал OMDb API, так как получить apiKey очень просто, и они дают вам доступ к большому количеству информации о фильмах.
Итак ... после того, как я немного изучил их API, я узнал, что мы можем отправить им идентификатор фильма IMDb в качестве параметра запроса, и они вернут JSON со всеми необходимыми нам данными. В нашем примере мы собираемся извлечь следующие свойства: Заголовок, Сюжет, Плакат, Дата выпуска, список всех жанров и imdbRating, разделенных запятыми.
Ниже вы увидите, как будет выглядеть MovieCard
Компонент:
class MovieCard extends React.Component { state = { movieData: {} }; render() { const { Title, Released, Genre, Plot, Poster, imdbRating } = this.state.movieData;
if (!Poster || Poster === 'N/A') { return null; }
return ( <div className="movie-card-container"> <div className="image-container"> <div className="bg-image" style={{ backgroundImage: `url(${Poster})` }} /> </div> <div className="movie-info"> <h2>Movie Details</h2> <div> <h1>{Title}</h1> <small>Released Date: {Released}</small> </div> <h4>Rating: {imdbRating} / 10</h4> <p>{Plot && Plot.substr(0, 350)}</p> <div className="tags-container"> {Genre && Genre.split(', ').map(g => ( <span key={g}>{g}</span> ))} </div> </div> </div> ); } }
Прежде чем двигаться дальше, я хотел бы объяснить несколько вещей:
- Мы устанавливаем
state.movieData
как пустой объект по умолчанию, чтобы избежать ошибок, когда мы пытаемся получить доступ к его свойствам в методеrender
. Без этогоmovieData
будетundefined
, и это вызовет ошибку. - Если Плакат не имеет значения или имеет значение
N/A
, мыreturn null
. Это гарантирует, что в этом случаеMovieCard
ничего не будет отображать. Мы бы не хотели видеть пустую карту, не так ли? :П - Мы устанавливаем Poster как
backgroundImage
элемента div. Это потому, что мы будем использоватьclip-path
в CSS, чтобы изображение выглядело закругленным. (Обратите внимание, что Clip-path не работает должным образом во всех браузерах, но если вы используете Chrome, все в порядке;)) - Вместо того, чтобы показывать весь Сюжет, я решил разрешить не более 350 символов. Вот почему я использовал на нем
.substring(0, 350)
. - Как я сказал выше, Жанр - это строка, содержащая список всех жанров, разделенных запятыми. Мы
split()
строку Жанр, используя запятую и пробел:', '
. Это вернет массив, который можноmapped
преобразовать в массив отдельных<span>
тегов, содержащих соответствующий жанр.
Кроме того, вы могли заметить, что мы используем метод короткого замыкания: Plot && Plot.substr(0, 350)
. Это гарантирует, что мы вызываем метод .substr()
на Plot
только в том случае, если Plot
не равно undefined
. Подробнее об этой технике читайте на MDN.
Вызов API
Пока все хорошо ... У нас есть Компонент, и теперь нам нужно сделать вызов API к конечной точке OMBd и получить нужные нам данные фильма. Для этого я собираюсь использовать Axios, поскольку он дает простой способ делать то, что мы хотим.
Мы добавим вызов axios
в жизненный цикл MovieCard
componentDidMount.
class MovieCard extends React.Component { state = { movieData: {} }; componentDidMount() { axios.get(`https://www.omdbapi.com/?apikey=${your_API}&i=${ this.props.movieID }&plot=full` ) .then(res => res.data) .then(res => { this.setState({ movieData: res }); }); }
// The rest of the code }
Axios.get()
вернет обещание, содержащее ответ. После этого, вызвав метод setState
, мы сохраняем ответ в movieData
.
Примечание:
- Для успешного вызова конечной точки API вам понадобится apikey. Вы можете получить его на сайте OMDb.
- Помимо apiKey мы также передаем идентификатор фильма IMDB в качестве параметра запроса
i=
. Это значение поступает изthis.props
, что означает, что когда мы будем использовать<MovieCard>
, нам придется передатьmovieID
как опору. Вы поймете, что я имею в виду, в следующем разделе. :)
Компонент MovieList
Этот компонент будет делать следующее:
- Укажите
form
сinput
, которые будут использоваться пользователем для отправки запроса поиска. - Используя термин search, он сделает запрос к конечной точке OMDb, чтобы получить список всех фильмов, которые содержат соответствующий термин в своих названиях.
- Отобразите результаты в виде списка
MovieCard
.
class MoviesList extends React.Component { state = { moviesList: ['tt2294629'], searchTerm: '' };
search = event => { event.preventDefault(); axios.get( `https://www.omdbapi.com/?apikey=${your_API}&s=${ this.state.searchTerm }&plot=full` ) .then(res => res.data) .then(res => { if (!res.Search) { this.setState({ moviesList: [] }); return; } const moviesList = res.Search.map(movie => movie.imdbID); this.setState({ moviesList }); }); };
handleChange = event => { this.setState({ searchTerm: event.target.value }); };
render() { const { moviesList } = this.state; return ( <div> <form onSubmit={this.search}> <input placeholder="Search for a movie" onChange={this.handleChange} /> <button type="submit"> <i className="fa fa-search" /> </button> </form> {moviesList.length > 0 ? ( moviesList.map(movie => ( <MovieCard movieID={movie} key={movie} /> )) ) : ( <p> Couldn't find any movie. Please search again using another search criteria. </p> )} </div> ); } }
У нас есть более крупный фрагмент кода выше. Давайте разберемся немного (по крайней мере, «важные» вещи: D):
Имеется два прослушивателя событий.
onChange
наinput
- это вызовет методhandleChange
, который будет обновлять состояниеsearchTerm
входным значением каждый раз, когда вход изменяется.onSubmit
наform
- это вызовет методsearch
, который сделает запрос к конечной точке API, предоставивsearchTerm
в качестве параметра запроса. Он сохранит данные ответа в массивmoviesList
(Обратите внимание, что мы сохраняем только значенияimdbID
из каждого возвращенного объекта, потому что это все, что нам нужно передать нашемуMovieCard
компоненту в качестве опоры).
Тем не менее, мы проверяем, есть ли какие-либо данные в res.Search
, в противном случае мы очищаем массив moviesList
.
В методе render
мы проверяем, есть ли в массиве хотя бы один элемент. Если этого не происходит, мы показываем пользователю сообщение об ошибке.
CSS
На этот раз я не буду вдаваться в подробности всей этой чепухи, которая есть в CSS, так как я не хочу делать сообщение слишком длинным. Но не стесняйтесь проверять код CSS на Github или Codepen.
Вкратце ... Я использовал flexbox
несколько раз, чтобы поместить элементы туда, где я хотел. Я также использовал clip-path
, чтобы сделать эту округлую форму изображения, и в конце я добавил немного запросов media
, чтобы MovieCard
выглядело лучше на мобильных устройствах.
В остальном весь код CSS довольно ясен. НО ... если вы хотите, чтобы я рассмотрел это и объяснил более подробно, дайте мне знать, и я обновлю сообщение! ;)
Заключение
Несмотря на то, что часть React не была такой сложной в создании, у меня были некоторые проблемы при попытке разместить все элементы там, где я хотел ... Самая большая проблема из всех заключалась в размещении закругленного изображения, поскольку оно продолжало получать поверх текста справа… и это работает не во всех браузерах, лол. Возможно, это была небольшая трата моего времени, так как мне, возможно, нужно было воссоздать его без использования clip-path
. :П
Тем не менее это был интересный проект! (как и большинство личных проектов, которые я создаю, ха-ха).
Вы можете найти его вживую на Codepen.
Дайте мне знать, что вы думаете. Что бы вы добавили, чтобы улучшить его? :)
Первоначально опубликовано на www.florin-pop.com.