react初学day04
小项目
根据之前的内容我们已经可以写一个小项目,主要是钩子函数的使用
这是我们的运行截图,连接的是豆瓣的api,获取每个地区的电影,点击的时候,去请求对应地区的数据。样式是使用的bootstrap,通过jq的ajax请求数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>react</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="../js/react.min.js"></script>
<script src="../js/react-dom.min.js"></script>
<script src="../js/browser.min.js"></script>
<script src="https://www.forgettime.cn/demo/js/jquery-1.10.1.min.js"></script>
<!-- js文件按顺序引入,还有我们的jq和bootstrap-->
</head>
<style>
.panel button{ margin: 0px 5px; }
.list-group{ width: 40%; float: left; }
</style>
<body>
<div id="app" class="container"></div>
<script type="text/babel">
let MovieBox = React.createClass({
getDefaultProps(){ //设置我们的初始属性,也就是展现在页面上的五个按钮
return {
citys:[
{id:1,title:"北京"},
{id:2,title:"上海"},
{id:3,title:"广州"},
{id:4,title:"深圳"},
{id:5,title:"青岛"},
]
}
},
getInitialState(){ //设置我们的初始属性,给选中哪个按钮设置一个参照值
return {
activeIndex:1
}
}, //下面注销的代码稍后再说
changeActiveIndex(index){ //这是第一种
/*if(index === this.state.activeIndex){return false;}*/
this.setState({
activeIndex: index
}) //改变我们选中按钮所对应的id,在这个方法在点击按钮的时候触发
},
renderCity(){
let {citys} = this.props; //解构的写法,类似于 let city = this.props.city
let {activeIndex} = this.state;
return citys.map((item)=>{ //我们没有v-for,只能通过map遍历数组,然后生成五个按钮
return (<button onClick={this.changeActiveIndex.bind(this,item.id)} className={"btn btn-default "+(item.id === activeIndex?'btn-danger':'')}>{item.title}</button>)
}) //className这里,去判断activeIndex和当前按钮的id是否相同,相同就active
//这里还有一个问题,changeActiveIndex这个方法需要穿一个参数,但是一写括号,就代表这里会触发,我们想的是,点的时候触发,但是又必须传参数,所以通过bind,既能够传参,又能让他在该触发的时候触发
},
getCity(){ //设置这个方法是因为,我们每次点击的时候请求的city是父组件传递
//给子组件的,所以待会将这个方法传递给子组件,让子组件通过这个属性去确认到底请求哪个城市的数据
let {citys} = this.props;
let {activeIndex} = this.state;
for(var i=0;i<citys.length;i++){
if(citys[i].id === activeIndex){
return citys[i].title;
}
}
},//这是第二种
/* shouldComponentUpdate(nextProps,nextState){
if(nextState.activeIndex === this.state.activeIndex){
return false;
}
return true;
}, */
render(){
return (
<div className="panel panel-primary">
<div className="panel-heading">
{this.renderCity()}
</div>
<MovieList city={this.getCity()}/> {/*将city传递给子组件*/}
</div>
)
}
});
let MovieList = React.createClass({
getInitialState(){
return {
movie:[]
}
},//这是第三种
componentWillReceiveProps(nextProps,nextState){
/* if(nextProps.city === this.props.city){
return false;
} */
this.getMovie(nextProps.city)
},
componentDidMount(){ //在这里请求数据,当然每次点按钮的时候也要请求数据,即componentWillReceiveProps
this.getMovie(this.props.city)
},
getMovie(city){//请求数据的方法
$.ajax({
url:"https://douban.uieee.com//v2/movie/in_theaters",
data:{
city
},
dataType: "jsonp", //解决jsonp格式的跨域的问题
success:(res)=>{
console.log(res);
this.setState({
movie:res.subjects
})
},
error:(err)=>{
console.log(err);
}
})
},
renderList(){
let{movie} = this.state;
if(!movie){return false};
return (
<ul className="list-group">
{movie.map((item)=>{
return (
<li className="list-group-item">{item.title}</li>
)
})}
</ul>
)
},
render(){
return (
<div className="panel-body">
{this.renderList()}
</div>
)
}
})
ReactDOM.render(<MovieBox/>,app);
</script>
</body>
</html>
这样,我们的小实例就完成了,其实现在有个小问题,多次点击会重复请求数据。
有三种方法,已经在代码中标注
- 可以在更改activeId的时候直接判断,如果传入的id与activeId相同就不需要setState了
- 在MovieBox组件中shouldComponentUpdate里状态做出判断,如果更改的id一样的话就不用向下执行,就不会给MovieContent传入新的属性 nextState.city === this.state.city return false(父组件render没有执行)
- 在MovieContent组件中componentWillReceiveProps里属性判断之后再去获取最新的城市所对应的电影数据 nextProps.city===this.props.city return false;
任何一个都可以