Need to make a call to youtube api to get information.
Which component should retrieve the list of videos? Downwards data flow: the most parent component should fetch the data.
index is the most parent component we have.
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import YTSearch from 'youtube-api-search';
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
YTSearch({key: API_KEY, term: 'surfboards'},function(data){
// call back function
console.log(data);
// check console.
});
const App = () => {
return (
<div>
<SearchBar />
</div>
);
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactoring Functional Components to Class Components
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
class App extends Component {
constructor(props){
super(props);
this.state = {videos: []}; // array of videos
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
this.setState({ videos });
// this.setState({videos: videos});
});
}
render(){
return (
<div>
<SearchBar />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Props
Will focus on list of videos
video_list.js
import React from 'react';
// add some classnames to add some styling
// add boostrap link in index.html
const VideoList = (props) => {
return (
<ul className="col-md-4 list-group">
{props.videos.length}
</ul>
);
}
export default VideoList;
in index.js
import VideoList from './components/video_list';
// passing props
render(){
return (
<div>
<SearchBar />
<VideoList videos={this.state.videos} />
</div>
);
}
Building Lists with Map
video_list.js
import React from 'react';
import VideoListItem from './video_list_item'
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return <VideoListItem video={video} />
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
/*
map example
var array = [1,2,3];
array.map((number) => {return number * 2})
array.map((number) => {return '<div>' + number + '</div>'})
*/
video_list_item.js
import React from 'react'
const VideoListItem = (props) => {
return <li>Video</li>
};
export default VideoListItem;
List item keys
in video_list.js add. Improve search efficiency
return <VideoListItem key={video.etag} video={video} />
Video List Items
video_list_item.js
import React from 'react'
const VideoListItem = ({video}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
Detail Component and Template Strings
Video Selection
index.js
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
// passing props
render(){
return (
<div>
<SearchBar />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
define a function that updates app state. takes a video and updates the selected video
pass this as a property 'onVideoSelect' into videolist
video_list.js
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.etag}
video={video} />
)
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
videolist takes the onVideoSelect property, and pass it into videolistitem
video_list_item.js
import React from 'react'
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
videolistitem takes the onVideoSelect property and says,
whenever i am clicked, call that function with the video that i was passed.
Such a call back shouldn't be more than two level deep'
it can get confusing from index.js all the way to video_list_item
Styling with CSS
style.css
.search-bar{
margin: 20px;
text-align: center;
}
.video-item input{
width: 64px;
}
.video-detail .details{
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.list-group-item{
cursor: pointer;
}
.list-group-item:hover{
background-color:#eee;
}
Make sure in search_bar.js, you have
<div className="search-bar">
Searching for videos
Add ability to search for new videos.
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
this.videoSearch('surfboards');
}
// passing props
render(){
return (
<div>
<SearchBar onSearchTermChange={(term => this.videoSearch(term))} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
videoSearch(term){
YTSearch({key: API_KEY, term: term}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactored youtube search in its own method, VideoSearch. Passed this method onto SearchBar under the property
onSearchTermChange. So Searchbar has to call props.onSearchTermChange.
, pass in a function, will call videoSearch with term.
When onSearchTermChange is called...
search_bar.js
import React from 'react';
class SearchBar extends React.Component{
constructor(props){
super(props);
this.state = {term: 'Starting Value'};
}
render(){
return (
<div className="search-bar">
<input
value = {this.state.term}
onChange={event => this.onInputChange(event.target.value)}
/>
</div>
)
}
onInputChange(term){
this.setState({term});
// fires off the callback function onsearchtermchange.
this.props.onSearchTermChange(term);
}
}
export default SearchBar;
Throttling Search Term Input
Its now laggy because search is called whenever we type something. UPdates are too often and overwhelming for users. WIll be good if we can throttlng.
1. need to install lodash
in terminal,
npm install --save lodash
2.
import _ from 'lodash';
3.
render(){
const videoSearch = _.debounce((term) => {this.videoSearch(term)},300);
return (
<div>
<SearchBar onSearchTermChange={videoSearch} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
want to call function once every ... milliseconds
we created a fat arrow function here and passed it to debounce. Debounce takes this inner function and returns a new function
that can only be called once every 300 milliseconds.
We then pass it into SearchBar.
No comments:
Post a Comment