案例要求:
达到这样一个效果
组件拆分:
- 搜索框是一个组件,传递状态
- 下面的展示列表是一个组件,使用状态
方式一:状态存在父组件之间
父组件传递给子组件消息可以使用props,子组件给父组件之间传递消息可以使用函数实现
APP组件代码
import React, { Component } from 'react'
import Search from './components/Search/Search'
import './App.css'
import List from './components/List/List'
export default class App extends Component {
state={ //初始化状态,状态初始化时要初始化对
items:[], //初始为数组
isFirst:true,
isLoading:false,
err:''
}
getData=(items) => {
this.setState({items:items})
}
setFirst=() => {
this.setState({isFirst:false})
}
setLoading=() => {
const {isLoading}=this.state
// this.state.setState({isLoading:!this.state.isLoading})
console.log(isLoading)
this.setState({isLoading:!isLoading})
}
setErr=(errInfo) => {
this.setState({err:errInfo})
}
render() {
const {items,isFirst,isLoading,err}=this.state
return (
<div className="container">
{/*这个地方传的时候可以一下子传一个对象,后面更改的时候只需传需要更改的字段就行,因为是覆盖的,不传就不会更改 */}
<Search getData={this.getData} setFirst={this.setFirst} setLoading={this.setLoading} setErr={this.setErr}/>
<List items={items} isFirst={isFirst} isLoading={isLoading} err={err}/>
</div>
)
}
}
search组件代码
import React, { Component } from 'react'
import axios from 'axios'
export default class Search extends Component {
search=() => {
//1、获取用户输入
const {input:{value:keyword}}=this
this.props.setFirst()
this.props.setLoading()
this.props.setErr("")//每次请求之前把err清空
//2、发送网络请求
axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then( //如果站的位置就是发送请求的位置,可以把http://localhost:3000省略
//这里面只能写response和error
response=>{
//这里面不函数后面不能写,不然会被当做表达式,在这里面不能setLoading,为什么这里的函数调不了
this.props.setLoading()
this.props.getData(response.data.items)
console.log('ccc')
},
error => {
// console.log('失败了',error),
this.props.setLoading()
this.props.setErr(error)
}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索github用户</h3>
<div>
<input ref={(currentNode) => {this.input=currentNode}} type="text" placeholder="输入关键词"/>
<button onClick={this.search}>搜索</button>
</div>
</section>
)
}
}
list组件代码
import React, { Component } from 'react'
import './List.css'
export default class List extends Component {
render() {
const {items}=this.props
if (this.props.isFirst) {
return <div><h2>欢迎来到该界面</h2></div>
}
if(this.props.isLoading){
return <div><h2>Loading.....</h2></div>
}
if(this.props.err!==""){
return <div><span>{this.props.err}</span></div>
}
return (
<div className="row">
{
items.map((item) => {
return (
<div className="card" key={item.id}>
<a href={item.html_url}>
<img alt="head_picture" src={item.avatar_url} style={{width: '100px'}}></img>
</a>
<p className="card-text">{item.login}</p>
</div>
)
})
}
</div>
)
}
}
方式二:消息订阅,该方式为常用方式,方便简单
状态在操作组件中定义,消息在传递状态的组件中发布,在使用状态的组件中订阅。使用PubSubJS库实现消息订阅。
搜索框组件代码
import React, { Component } from 'react'
import axios from 'axios'
import PubSub from 'pubsub-js'
export default class Search extends Component {
// //1、获取用户输入
// const {input:{value:keyword}}=this
// this.props.setFirst()
// this.props.setLoading()
// this.props.setErr("")//每次请求之前把err清空
// //2、发送网络请求
// axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then( //如果站的位置就是发送请求的位置,可以把http://localhost:3000省略
// //这里面只能写response和error
// response=>{
// //这里面不函数后面不能写,不然会被当做表达式,在这里面不能setLoading,为什么这里的函数调不了
// this.props.setLoading()
// this.props.getData(response.data.items)
// console.log('ccc')
// },
// error => {
// // console.log('失败了',error),
// this.props.setLoading()
// this.props.setErr(error)
// }
// )
search=() => {
const {input:{value:keyword}}=this
PubSub.publish('state',{isFirst:false,err:'',isLoading:true}) //发布消息
axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then(
response => {
PubSub.publish('state',{isLoading:false,items:response.data.items})
},
error =>{
PubSub.publish('state',{isLoading:false,err:error})
}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索github用户</h3>
<div>
<input ref={(currentNode) => {this.input=currentNode}} type="text" placeholder="输入关键词"/>
<button onClick={this.search}>搜索</button>
</div>
</section>
)
}
}
展示列表组件代码:
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './List.css'
export default class List extends Component {
state={ //初始化状态,状态初始化时要初始化对
items:[], //初始为数组
isFirst:true,
isLoading:false,
err:''
}
componentDidMount(){
//组件挂载到页面后,该函数开启定时器或者订阅消息
PubSub.subscribe('state',(_,data) => {
//这里返回的token是供componentWillUnmount()关闭定时器或者取消订阅时候使用的
this.token=this.setState(data) //data是一个状态对象,订阅消息后,当组件卸载的时候,还要取消订阅
})
}
componentWillUnmount(){
//组件即将卸载的时候,该函数关闭定时器,或者取消订阅
PubSub.unsubscribe(this.token)
}
render() {
const {isFirst,isLoading,err,items}=this.state
{/**
if (this.props.isFirst) {
return <div><h2>欢迎来到该界面</h2></div>
}
if(this.props.isLoading){
return <div><h2>Loading.....</h2></div>
}
if(this.props.err!==""){
return <div><span>{this.props.err}</span></div>
}
return (
<div className="row">
{
items.map((item) => {
return (
<div className="card" key={item.id}>
<a href={item.html_url}>
<img alt="head_picture" src={item.avatar_url} style={{width: '100px'}}></img>
</a>
<p className="card-text">{item.login}</p>
</div>
)
})
}
</div>
)
*/}
return (
<div className="row">
{
//连着写的三元表达式,不能用if语句,因为jsx里面只能写js表达式,不能写js语句
isFirst?<h2>欢迎使用,输入关键字,随后点击搜索</h2>:
isLoading?<h2>Loading.....</h2>:
err?<h2 style={{color:'red'}}>{err}</h2>:
items.map((item) => {
return (
<div className="card" key={item.id}>
<a href={item.html_url}>
<img alt="head_picture" src={item.avatar_url} style={{width: '100px'}}></img>
</a>
<p className="card-text">{item.login}</p>
</div>
)
})
}
</div>
)
}
}