GraphQL 및 React를 사용하여 영화 사이트 만들기 - 두 번째 섹션

part one년에 GraphQL API를 만들었습니다.이제 이 API를 사용하는react 응용 프로그램을 만들 것입니다.
계속하기 전에 저는 멋있다고 생각했기 때문에 HTTP 클라이언트, 예를 들어axios를 사용하여 GraphQL 서버에 요청할 수 있습니다!이것 좀 봐:
const query = `{
    newMovies {
      id
    title
    }  
}`
const url = 'http://localhost:4000/graphql?query='+query;

axios.get(url)
  .then(res => console.log(res.data.data.newMovies))
관심 있으시면 graphQL 인터페이스를 사용할 때 url의 변화에 주의하여 설정의 실제 상황을 보실 수 있습니다. - 연구를 진행했습니다.
그러나 생산을 더욱 쉽고 즐겁게 하기 위해 우리는 GraphQL 클라이언트를 사용하여 HTTP 클라이언트를 대체할 수 있다.
선택할 수 있는 고객은 매우 적다.이 강좌에서 나는 Apollo Client을 사용할 것이다.Apollo도graphQL 서버를 제공했지만 express-graphql으로 만들었기 때문에 Apollo의 이 부분을 사용하지 않고 Apollo 클라이언트입니다. 말 그대로react에서graphQL을 작성할 수 있습니다.

너트 케이스에서


계속하려면 github 복제 저장소에서 지점 이름 Graphql-api을 서명해야 합니다. 왜냐하면react단을 주목하기 때문에 모든 코드는 client 디렉터리, 즉react 응용 프로그램 코드에 기록됩니다.
분명히 이것은 초보자의 강좌가 아니다.만약 네가 반응을 모르지만, 내가 쓴 기초 지식을 배우는 것에 흥미가 있다.
먼저 다음 패키지를 설치합니다.
npm install apollo-boost react-apollo graphql-tag graphql --save
게임 계획은 ApolloProvider으로 우리의react 응용 프로그램을 포장하고 GraphQL 클라이언트를react 도구에 추가하는 것입니다.그리고 graphql-tag을 통해 graphQL 조회를 진행합니다.
현재 ./client/index.js에서 이 설정을 볼 수 있습니다
import React from 'react';
import ReactDOM from 'react-dom';
import './style/style.scss';
const App = () => {
  return <div>Hello World2</div>
}
ReactDOM.render(
  <App />,
  document.querySelector('#root')
);
첫 번째 단계는 전체 응용 프로그램을 ApolloProvider으로 포장하는 것이다.공급자는 반응하기 위해 GraphQL 클라이언트가 필요합니다.
import { ApolloProvider, graphql } from 'react-apollo';
...
const client = new ApolloClient({
  uri: "http://localhost:4000/graphql"
});
ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider> ,
  document.querySelector('#root')
);
GraphQL 서버가 ApolloClient을 가리키지 않으면 uri/graphql이 필요합니다.그래서 우리의 예에서 그것을 없애고 new ApolloClient()만 사용하면 된다
이제 클라이언트에 액세스하여 다음과 같이 질의할 수 있습니다.
import { ApolloProvider, graphql } from 'react-apollo';
import gql from 'graphql-tag';
import ApolloClient from 'apollo-boost';

const AppComponent = (props) => {
  if(props.data.loading) return '<div>loading</div>';
  return <div>{props.data.newMovies[0].title}</div>
}
const query = gql`{ newMovies { title } }`;
const App = graphql(query)(AppComponent)
우리는 AppComponent으로 graphql을 포장하고 검색을 도구에 주입한 후에 props.data.newMovies을 우리에게 영화 결과를 주었다.

시작합시다.


우리가 구축하고 있는 응용 프로그램은 위에 표시된 하나의 제목보다 예시가 더 크기 때문에 분리합니다../client/index.js부터.
import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
import { HashRouter, Switch, Route } from 'react-router-dom'
import NewMovies from './components/NewMovies';
import './style/style.scss';
const client = new ApolloClient();
const Root = () => {
  return (
    <HashRouter >
      <ApolloProvider client={client}>
      <Switch >
        <Route exact path="/" component={NewMovies} />
      </Switch>
      </ApolloProvider>
    </HashRouter>
  )
}
ReactDOM.render(
  <Root />,
  document.querySelector('#root')
);
간단하다가져온 구성 요소(NewMovies)는 존재하지 않지만, 이것이 ./client/index.js에 필요한 모든 코드입니다.
마찬가지로 우리가 사용할 모든 구성 요소는 Switch 구성 요소에 지정됩니다.따라서 전체 응용 프로그램은 ApolloProvider으로 포장되어nutshell 부분과 완전히 같다.

최고의 영화 얻기

./client/components/NewMovies.js에 파일을 만들고 필요한 패키지를 가져오는 것부터 시작합시다
import React, { Component} from 'react'
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
다음은 newMovies GraphQL 검색 결과를 NewMovies 구성 요소에 주입합니다
class NewMovies extends Component {
...
}
const query = gql`
{
    newMovies {
        id
        poster_path
        title
    }
}
`
export default graphql(query)(NewMovies);
이 설정을 통해 하나의 대상 수조에 NewMovies 구성 요소 도구가 주입되고 this.props.data.newMovies에 접근할 수 있습니다.이를 활용하자:
class NewMovies extends Component {
    Movies(){
        return this.props.data.newMovies.map(movie => {
            return (
                <article key={movie.id} className="movie_list">
                    <img src={movie.poster_path} />
                    <h1>{movie.title}</h1>
                </article>
            );
        })
    }
    render() {
        if(this.props.data.loading) return <div>loading</div>
        return this.Movies()
    }
}
찾았습니다.주의해야 할 것은
  • newMovies 결과를 얻기 전에 반응 구성 요소를 불러옵니다.
  • graphql는 우리에게 loading 속성을 제공했다. 이 속성은 데이터를 얻을 때 true으로 설정하고 데이터가 준비되어 사용할 때 false
  • 으로 설정한다.
    우리가 다른 구성 요소를 계속 토론하기 전에, 포스터를 선택할 때 더 많은 정보를 얻을 수 있도록 닻으로 영화 포스터를 포장합시다.
    이를 위해 Link 패키지의 react-router-dom 구성 요소를 사용합니다.
    import { Link } from 'react-router-dom'
    
    class NewMovies extends Component {
        Movies(){
            return this.props.data.newMovies.map(movie => {
                return (
                    <article key={movie.id} className="movie_list">
                        <Link to={"/info/"+movie.id}> 
                            <img src={movie.poster_path} />
                        </Link>
        ...
    
    예를 들어 포스터를 클릭할 때마다 /info/1으로 안내됩니다.
    이 루트를 포착하는 공유기를 추가하려면 ./client/index.js으로 돌아가야 합니다.
    ...
    import MovieInfo from './components/MovieInfo';
    ...
    const Root = () => {
      return (
        <HashRouter >
          <ApolloProvider client={client}>
          <Switch >
            <Route exact path="/" component={TopMovies} />
            <Route exact path="/info/:id" component={MovieInfo} />
          </Switch>
          ...
    
    물론,react 루트 () 의 기능입니다.

    Movie Info 구성 요소를 살펴보도록 하겠습니다.


    먼저 ./client/components/MovieInfo.js에 파일을 만들고 다음을 추가합니다.
    import React, { Component } from 'react'
    import gql from 'graphql-tag'
    import { graphql } from 'react-apollo'
    class MovieInfo extends Component {
        render(){
            if(this.props.data.loading) return <div>loading</div>
            return (
                <div>{this.props.data.movieInfo.title}</div>
            )
        }
    }
    const query = gql`
    {movieInfo(id: "284054") {
            title
    }}`;
    
    export default graphql(query)(MovieInfo);
    
    그것은 정상적으로 일할 수 있습니까?
    우리는 하드코딩된 id을 조회하고 있습니다. 이것은 우리가 원하는 것이 아니라, 반대로, 우리의react 구성 요소 도구에서graphql 조회에 ID를 전달하고자 합니다.react-apollo은 우리에게 Query 구성 요소를 제공하여 우리가 이 점을 할 수 있도록 한다.
    import { Query, graphql } from 'react-apollo'
    class MovieInfo extends Component {
        render(){
            const id = this.props.match.params.id;
            return (
                <Query query={query} variables={{id}} >
                {
                    (({loading, err, data}) => {
                        if(loading) return <div>loading</div>
                        return (
                            <div>{data.movieInfo.title}</div>
                        )
                    })
                }
                </Query>
            )
        }
    }
    const query = gql`
    
    query MovieInfo($id: String) {
        movieInfo(id: $id) {
            title
          }
      }
    `;
    
    거의 똑같지만 Query이 있으면 우리는 변수를 전달할 수 있다.
    이제 구성 요소의 나머지 부분을 개발합시다.Query 내에 다음 코드를 반환합니다
    return(
        <div>
            <header style={{backgroundImage:  'url("https://image.tmdb.org/t/p/w500///'+data.movieInfo.poster_path+'")'}}>
                <h2 className="title">{data.movieInfo.title}</h2>
            </header>
            <article className="wrapper">  
                <p className="description">{data.movieInfo.overview}</p>                
                <div className="sidebar">
                    <img src={"https://image.tmdb.org/t/p/w500///"+data.movieInfo.poster_path} className="cover_image" alt="" />
                    <ul>
                        <li><strong>Genre:</strong> {data.movieInfo.genres}</li>
                        <li><strong>Released:</strong>{data.movieInfo.release_date}</li>
                        <li><strong>Rated:</strong> {data.movieInfo.vote_average}</li>
                        <li><strong>Runtime:</strong> {data.movieInfo.runtime}</li>
                        <li><strong>Production Companies:</strong> {data.movieInfo.production_companies}</li>
                    </ul>
                    <div className="videos">
                        <h3>Videos</h3>
                        {/* videos */}
                    </div>
                        {/* reviews */} 
                </div>
                    {/* credits */}                                         
            </article>
        </div>
    )
    
    보시다시피 요청하지 않은 검색 속성에 접근하려고 시도하고 있습니다.이 명령을 실행하면 요청이 실패하면 404 오류가 발생합니다.따라서 title보다 더 많은 속성을 요청하기 위해 조회를 업데이트해야 합니다.
    query MovieInfo($id: String) {
        movieInfo(id: $id) {
            title
            overview
            poster_path
            genres
            release_date
            vote_average
            runtime
            production_companies
          }
      }
    `;
    
    이 업데이트와git 저장소에서 제공할 css가 있습니다. 우리가 계속 하고 있는 부분은 다음과 같습니다.

    코드 주석에서 보듯이 우리는 페이지에 동영상, 평론, 신용을 추가해야 한다.

    비디오 추가


    에서 GraphQL 쿼리를 설계하여 movieInfo 쿼리에서 비디오를 얻을 수 있도록 합니다.먼저 이렇게 합시다.
    const query = gql`
    query MovieInfo($id: String) {
        movieInfo(id: $id) {
            ...
            videos {
                id 
                key
            }
          }
      }
    `;
    
    이 영상들은 때때로 한 개에 그치지 않기 때문에 진열 형식으로 나타난다.따라서 이 수조를 처리하는 가장 좋은 방법은 MovieInfo 구성 요소 안에 모든 영상을 되돌려주는 단독 방법을 만드는 것이다.
    class MovieInfo extends Component {
        renderVideos(videos){
            return videos.map(video => {
                return (
                    <img key={video.id} 
                        onClick={()=> this.videoDisplay(video.key)} 
                        className="video_thumbs" 
                        src={`http://img.youtube.com/vi/${video.key}/0.jpg`}
                    />
                )
            })
        }
        render(){
            ...
            {/* videos */}
            {this.renderVideos(data.movieInfo.videos)}
            ...                     
    
    첫 번째 강좌에서 설명한 바와 같이 key 대상의 videos은 유튜브 영상 ID를 가리킨다. 유튜브는 이 특정 형식의 캡처 이미지를 사용할 수 있게 한다(src 속성에서 전달).또한 앞에서 언급한 바와 같이 우리가 ID를 사용하는 이유는 바로 key의 독특한 기능-React가 필요하다는 것을 알고 있기 때문이다.
    사용자가 이 축소판을 눌렀을 때, 나는 화면에 유튜브 영상인 onClick={()=> this.videoDisplay(video.key)}을 불러오고 싶었다.이 기능을 만듭니다.
    우리가 이 점을 실현하는 방법은 상태를 바꾸는 것이다
    class MovieInfo extends Component {
        constructor(){
            super();
            this.state={
                video: null
            }
        }
        videoDisplay(video){
            this.setState({
                video
            })
        }
        videoExit(){
            this.setState({
                video: null
            })
        }
        ...
    
    페이지가 video을 불러올 때 상태가 null이고 미리 보기 그림을 누르고 videoDisplay을 터치하면 video 상태가 유튜브 영상 key을 값으로 합니다.videoExit 메서드를 터치하면 video 상태가 null으로 리셋됨을 볼 수 있습니다
    마지막으로 상태가 바뀔 때 동영상을 표시하는 방법이 필요하기 때문에 다른 방법을 만듭니다.위 방법 아래에서 이 방법을 추가합니다.
    videoToggle(){
        if(this.state.video) return(
            <div className="youtube-video">
                <p onClick={() => this.videoExit()}>close</p>
                <iframe  width="560" height="315" src={`//www.youtube.com/embed/${this.state.video}` } frameborder="0" allowfullscreen />
            </div>
        ) 
    }
    
    그리고 간단하게 페이지의 모든 위치에 보여줍니다
    <div className="videos">
        {this.videoToggle()}
        <h3>Videos</h3>
        {this.renderVideos(data.movieInfo.videos)}
    </div>
    
    마찬가지로 video 상태가 null이면 {this.videoToggle()}은 아무런 조작도 하지 않습니다.상태가 비어 있지 않으면 - video에 키가 있으면 {this.videoToggle()}으로 비디오를 렌더링합니다.

    영화 학점과 평론을 추가하다


    나는 영화 평론과 영화 학점을 각자의 독립된 부분에 두기로 결정했다.빈 구성 요소 파일을 만들고 MovieInfo 구성 요소에서 가져오고 사용하며 조회를 업데이트합니다../client/components/MovieInfo.js에 변경 내용 추가
    import MovieReviews from './MovieReviews'
    import MovieCredits from './MovieCredits'
    
    class MovieInfo extends Component {
    ...
    {/* reviews */}
        <MovieReviews reviews={data.movieInfo.movieReviews} />  
        </div>
            {/* credits */}
            <MovieCredits credits={data.movieInfo.movieCredits} />              
    </article>
    }
    ...
    
    const query = gql`
    
    query MovieInfo($id: String) {
        movieInfo(id: $id) {
            ...
            movieReviews {
                id
                content
                author
            }
            movieCredits{
                id
                character
                name
                profile_path
                order
              }
          }
      }
    `;
    ...
    
    우리는 movieReviewsmovieCredits 조회에서 데이터를 얻어 각각의 구성 요소에 전달합니다.지금 저희는 그냥 데이터를 빨리 보여주는 거예요.

    영화 학점 구성 요소


    다음 코드를 ./client/components/MovieCredits.js에 추가
    import React, { Component } from 'react'
    export class MovieCredits extends Component {
        renderCast(credits){
            return credits.map(cast => {
                return (
                    <li key={cast.id}>
                        <img src={`https://image.tmdb.org/t/p/w500//${cast.profile_path}`} />
                        <div className="castWrapper">
                            <div className="castWrapperInfo">
                                <span>{cast.name}</span>
                                <span>{cast.character}</span>
                            </div>
                        </div>
                    </li>
                )
            })
        }
      render() {
        return (<ul className="cast">{this.renderCast(this.props.credits)}</ul>)
      }
    }
    export default MovieCredits
    
    위에서 아무런 새로운 해석도 없다

    영화 평론 구성 요소


    다음 코드를 ./client/components/MovieReviews.js에 추가
    import React, { Component } from 'react'
    class MovieReviews extends Component {
        renderReviews(reviews){
            return reviews.map(review => {
                return (
                    <article key={review.id}><h4>{review.author} writes</h4>
                        <div>{review.content}</div>
                    </article>
                )
            })
        }
        render() {
            return(
                <div className="reviews">
                    {this.renderReviews(this.props.reviews)}
                </div>  
            )
        }
    } 
    export default MovieReviews;
    
    이렇게이것이 바로 학점, 동영상, 평론의 표시 방식이다.

    결론


    전체 응용 프로그램 (예를 들어 it 브래킷) 은 같은 저장소에서 찾을 수 있으며, demo here을 볼 수 있습니다.그것은 세 개의 분기 react-app branchmaster branch에 완전한 코드를 가지고 있으며, 모든 강좌 건축은 서로의 꼭대기에 있다.그 중에서 Graphql-api branch의 코드는

    좋은 웹페이지 즐겨찾기