现代web页面里到处都是ajax,所以处理好异步的代码非常重要。
这次我重新选了个最适合展示异步处理的应用场景——搜索新闻列表。由于有现成的接口,我们就不用自己搭服务了。 我在网上随便搜到了一个新闻服务接口,支持jsonp,就用它吧。
一开始,咱们仍然按照action->reducer->components的顺序把基本的代码写出来。先想好要什么功能, 我设想的就是有一个输入框,旁边一个搜索按钮,输入关键字后一点按钮相关的新闻列表就展示出来了。
首先是action,现在能想到的动作就是把新闻列表放到仓库里,至于列表数据是哪儿来的一会儿再说。 来看src/actions/news.js:
import {cac} from 'utils'
export const PUSH_NEWS_LIST = 'PUSH_NEWS_LIST'
export const pushList = cac(PUSH_NEWS_LIST, 'list')
然后是reducer,没什么特别的,只要遇到上面定义的那个action,就把数据放到相应的状态里就行了。 我们先定一个叫做news的状态,里面再包含一个子状态list。后面还要扩充功能,还会给news状态添加更多的子状态。 以下是src/reducers/news.js的代码:
import {combineReducers} from 'redux';
import {cr} from '../utils'
import {PUSH_NEWS_LIST} from 'actions/news'
export default combineReducers({
list: cr([], {
[PUSH_NEWS_LIST](state, {list}){return list}
})
})
现在就可以开始写组件了。这回我们要做的是个列表,也就是要有重复的东西,我想最好把重复的东西单抽取成一个组件以便维护和复用。 那就把一条新闻抽取成一个组件吧,它应该具有标题、发布时间、图片以及概述这些内容。 这个组件绝对是纯纯的,不用跟外界打交道,所以把它放到components目录里。src/components/NewsOverview.js:
import React from 'react';
class NewsOverview extends React.Component {
render(){
let date = new Date(this.props.time)
return (
{this.props.title}
{date.toLocaleDateString()} {date.toLocaleTimeString()}
{this.props.description}
)
}
}
export default NewsOverview
然后写要跟外界打交道的组件,这个组件需要响应用户的点击按钮的事件,发起获取新闻列表的请求,然后把数据放到页面里。 src/containers/newsList.js:
import React from 'react';
import { connect } from 'react-redux'
import NewsOverview from 'components/NewsOverview'
import {pushList} from 'actions/news'
class NewsList extends React.Component {
search(){
let keyword = this.refs.keyInput.value
// TODO: 获取新闻列表
}
renderList(){
return this.props.list.map(item =>{
item.key = item.title
return React.createElement(NewsOverview, item)
})
}
render(){
return (
)
}
}
function mapStateToProps(state) {
// 一般一组状态都是为一个页面服务的,所以把它们一股脑的映射过来比较方便
// 但是把映射一一写出来也有好处,就是很容易看到组件里有什么属性
return Object.assign({}, state.news)
}
export default connect(mapStateToProps)(NewsList);
代码差不多了,但是它现在没法工作,因为我们还没给添加ajax请求的代码。最简单粗暴的方法就是在上面的search方法中直接来个ajax请求, 然后在回调中派发“PUSH_NEWS_LIST”的action。也行。先写出来吧。为了简化ajax代码,我在src/index.html里面引入了jQuery。 当然,用了react,我们也许用不上jQuery的其他功能,所以用fetch或者其它ajax库都行。
search(){
let keyword = this.refs.keyInput.value
window.$.ajax({
url: 'http://www.tngou.net/api/search',
data: { keyword, name: 'topword' },
dataType: 'jsonp',
success: (data)=>{
if(data.status)
this.props.dispatch(pushList(data.tngou))
}
})
}
最后别忘了修改入口、添加reducer:把src/index.js里面Provider下面的组件换成NewsList; 在src/reducers/index.js里面引入新增的reducer,并加到reducers对象里。