React - Review - 09

React - Review - 09

Review React

지금까지 배운 내용으로 Youtube API를 가져와 화면에 출력해주는 프로젝트를 만든다.

Accessing the Youtube API

  • console.developers.google.com 접속
  • 프로젝트 생성 (CORS off!)
  • 사용할 API Library 선택 -> 사용 설정 버튼 클릭
  • 메뉴에서 사용자 인증 장소 선택 -> CREATE CREDENTIAL 선택 -> 프로젝트에 맞도록 선택 후 확인 버튼 클릭
  • 사용자 인증 정보 복사(API key)
  • AIzaSyD1pBSnrieKIUvBwQACF2f0bzvZum1C0AY
  • 키 제한사항 -> HTTP referrers(web sites) -> localhost:3001 작성 (다른 사람이 사용할 수 있기 때문)
// src/apis/youtube
import  axios  from  'axios';
  
const  KEY = 'Your API Key';

export  default  axios.create({
 baseURL:  'https://www.googleapis.com/youtube/v3',
 params: {
  part:  'snippet',
  maxResults:  5,
  key:  KEY,
  q:  'surfboards'
 }
});

Passing Props Parent Component

// src/App.js
import  React  from  'react';
import  SearchBar  from  './SearchBar';
import  youtube  from  '../apis/youtube';
  
class  App  extends  React.Component {
 onTermSubmit = term  => {
  // console.log(term);
  youtube.get('/search', {
   params: {
    q:  term
   }
  });
 };

 render() {
  return (
   <div  className="ui container">
    <SearchBar  onFormSubmit={this.onTermSubmit} />
   </div>
  )
 };
}

export  default  App;

// src/components/SearchBar.js
import  React  from  'react';

class  SearchBar  extends  React.Component {
 state = { term:  '' };

 onInputChange = event  => {
  this.setState({ term:  event.target.value });
 };

 onFormSubmit = event  => {
  event.preventDefault();
  this.props.onFormSubmit(this.state.term ); // this
 };

 render() {
  return (
   <div  className="search-bar ui segment">
    <form  onSubmit={this.onFormSubmit} className="ui form">
     <div  className="field">
      <label>Video  Search</label>
      <input
       type="text"
       value={this.state.term}
       onChange={this.onInputChange}
      />
     </div>
    </form>
   </div>
  );
 }
}

export  default  SearchBar;
Network tab에서 아래와 같이 데이터가 넘어 오는 것을 확인할 수 있다.
data

Updating State with Fetched Data

// src/App.js
import  React  from  'react';
import  SearchBar  from  './SearchBar';
import  youtube  from  '../apis/youtube';

class  App  extends  React.Component {
 state = { videos: [] };

 onTermSubmit = async  term  => {
  const  response = await  youtube.get('/search', {
   params: {
    q:  term
   }
  });
  
  this.setState({ videos:  response.data.items }); // this
 };

 render() {
  return (
   <div  className="ui container">
    <SearchBar  onFormSubmit={this.onTermSubmit} />
I  have {this.state.videos.length} videos.
   </div>
  )
 };
}

export  default  App;

Passing State as Props

Add VideoList.js
// src/components/VideoList.js
import  React  from  'react';

const  VideoList = props  => {
 return (
  <div>{props.videos.length}</div>
 )
};

export  default  VideoList;

// scr/App.js
render() {
 return (
  <div  className="ui container">
   <SearchBar  onFormSubmit={this.onTermSubmit} />
   <VideoList  videos={this.state.videos} />
  </div>
 )
};

Rndering a List of Videos

Add VideoItem.js
// src/components/VideoItem.js
import  React  from  'react';

const  VideoItem = (props) => {
 return (
  <div>Video  Item</div>
 )
};

export  default  VideoItem;

// src/components/VideoList.js
import  React  from  'react';
import  VideoItem  from  './VideoItem';

const  VideoList = ({ videos }) => {
 const  renderedList = videos.map((video) => {
  return <VideoItem />
 });
 
 return (
  <div>{renderedList}</div>
 )
};

export  default  VideoList;

Rendering Video Thumbnails and Styling a List

// src/components/VideoItem.js
import  React  from  'react';

const  VideoItem = ({ video }) => {
 return (
  <div  className="video-item item">
   <img  className="ui image"  src={video.snippet.thumbnails.medium.url} />
   <div  className="content">
    <div  className="header">{video.snippet.title}</div>
   </div>
  </div>
 );
};

export  default  VideoItem;

// src/components/VideoList.js
import  React  from  'react';
import  VideoItem  from  './VideoItem';

const  VideoList = ({ videos }) => {
 const  renderedList = videos.map((video) => {
  return <VideoItem  video={video} />
 });

 return (
  <div  className="ui relaxed divided list">{renderedList}</div>
 )
};

export  default  VideoList;
/* /src/components/VideoItem.css */
.video-item {
 display: flex  !important;
 align-items: center  !important;
 cursor: pointer;
}

.video-item.item  img {
 max-width: 180px;
}
9-2

Conditional Rendering

// src/App.js
state = { videos: [], selectedVideo:  null };

onVideoSelect = (video) => {
 // console.log('From the App!', video);
 this.setState({ selectedVideo:  video });
};

render() {
 return (
  <div  className="ui container">
   <SearchBar  onFormSubmit={this.onTermSubmit} />
   <VideoDetail  video={this.state.selectedVideo} />
   <VideoList  onVideoSelect={this.onVideoSelect} videos={this.state.videos} />
  </div>
 )
};
  
// src/components/VideoList.js
const  VideoList = ({ videos, onVideoSelect }) => {
 const  renderedList = videos.map((video) => {

  return <VideoItem  onVideoSelect={onVideoSelect} video={video} />
 });

 return (
  <div  className="ui relaxed divided list">{renderedList}</div>
 )
};

// src/components/VideoItem.js
const  VideoItem = ({ video, onVideoSelect }) => {
 return (
  <div  onClick={() => onVideoSelect(video)} className="video-item item">
   <img  className="ui image"  src={video.snippet.thumbnails.medium.url} />
   <div  className="content">
    <div  className="header">{video.snippet.title}</div>
   </div>
  </div>
 );
};

// src/components/VideoDetail.js
import  React  from  'react';

const  VideoDetail = ({ video }) => {
 if (!video) {
  return <div>Loading...</div>
 }
 return (
  <div>{video.snippet.title}</div>
 );
};

export  default  VideoDetail;
9-3

Displaying a Video Player

// src/components/VideoDetail.js
import  React  from  'react';

const  VideoDetail = ({ video }) => {
 if (!video) {
  return <div>Loading...</div>
 }

 const  videoSrc = `https://www.youtube.com/embed/${video.id.videoId}`; // this
 return (
  <div>
   <div  className="ui embed">
    <iframe  src={videoSrc} />
   </div>
   <div  className="ui segment">
    <h4  className="ui header">{video.snippet.title}</h4>
    <p>{video.snippet.description}</p>
   </div>
  </div>
 );
};

export  default  VideoDetail;
9-4

Fixing a Few Warnings

console에 Each child in an array or iterator should have a unique "key" prop.라는 에러와 여러 경고들이 출력된다.
// src/components/VideoItem.js
<img  alt={video.snippet.title} className="ui image"  src={video.snippet.thumbnails.medium.url} />

// src/components/VideoList.js
const  renderedList = videos.map((video) => {
 return <VideoItem  key={video.id.videoId} onVideoSelect={onVideoSelect} video={video} />
});

// src/components/VideoDetail.js
<iframe  title="video player"  src={videoSrc} />

// css -> src/App.js
render() {
 return (
  <div  className="ui container">
   <SearchBar  onFormSubmit={this.onTermSubmit} />
   <div  className="ui grid">
    <div  className="ui row">
     <div  className="eleven wide column">
      <VideoDetail  video={this.state.selectedVideo} />
     </div>
     <div  className="five wide column">
      <VideoList  onVideoSelect={this.onVideoSelect} videos={this.state.videos} />
     </div>
    </div>
   </div>
  </div>
 )
}
9-5

Defaulting Video Selection

새로고침하면 buildings 검색어가 자동 검색되도록 수정한다.
// src/App.js
componentDidMount() {
 this.onTermSubmit('buildings');
};

onTermSubmit = async  term  => {
 const  response = await  youtube.get('/search', {
  params: {
   q:  term
  }
 });

 this.setState({
  videos:  response.data.items,
  selectedVideo:  response.data.items[0] // this
 });
};

GitHub and Others

댓글

가장 많이 본 글