分为三个组件
- SearchBox:搜索框组件
- PopularSearch:搜索流行词组件
- SearchHistory:搜索历史组件
- 搜索框输入关键词,前端发送关键词请求,后端返回对应的查询数据
- 显示流行词数据
- 记录搜索历史
搜索页面存在着两个网络请求
getPopularKeywords: () => `http://localhost:3000/mock/keywords/popular.json`,
getRelatedKeywords: (text) => `http://localhost:3000/mock/keywords/related.json?keyword=${text}`
在搜索关键词state设计的时候遇到了比较麻烦的问题
因为在发送关键词请求getRelatedKeywords(text)
的时候,后端返回的应该是这样一串数据
[
{ "id": "k-10", "keyword": "火锅", "quantity": 8710 },
{ "id": "k-11", "keyword": "火锅自助", "quantity": 541 },
{ "id": "k-12", "keyword": "火锅 三里屯", "quantity": 65 },
{ "id": "k-13", "keyword": "火锅 望京", "quantity": 133 },
...
]
如果将relatedKeywords
的state设计成和popularKeywords
一样
relatedKeywords: {
isFetching: false,
ids: [] // 保存id
},
relatedKeywords: {
isFetching: false,
ids: [] // 保存id
}
就无法缓存搜索结果,因为每次请求都得清空之前的关键字ids
,然后保存新的关键字ids
就会导致用户下次再次进行某个相同关键字的搜索时,就需要重新发送请求,影响用户体验
所以设计的时候得嵌套一层
relatedKeywords: {
'关键字1': {
isFetching: false,
ids: [] // 保存id
},
'关键字2': {
isFetching: false,
ids: [] // 保存id
}
}
嵌套了一层,处理reducer就比较麻烦了,需要创建一个新的子reducer,但这样做前端就通过redux建立起了关键词索引
const relatedKeywords = (state = initState.relatedKeywords, action) => {
switch (action.type) {
case types.FETCH_RELATED_KEYWORDS_REQUEST:
case types.FETCH_RELATED_KEYWORDS_SUCCESS:
case types.FETCH_RELATED_KEYWORDS_FAILURE:
return {
...state,
[action.text]: relatedKeywordsByText(state[action.text], action)
}
default:
return state
}
}
// related的子reducer
const relatedKeywordsByText = (
state = { isFetching: false, ids: [] },
action
) => {
switch (action.type) {
case types.FETCH_RELATED_KEYWORDS_REQUEST:
return { ...state, isFetching: true }
case types.FETCH_RELATED_KEYWORDS_SUCCESS:
return {
...state,
isFetching: false,
ids: state.ids.concat(action.response.ids)
}
case types.FETCH_RELATED_KEYWORDS_FAILURE:
return { ...state, isFetching: false }
default:
return state
}
}
这一点也印证了,尽量把state设计扁平化,才利于我们的开发
显示流行词数据,记录搜索历史 这两个功能相对而言就比较容易了
在学的过程中发现这个例子写出来其实是性能有问题的
在使用Chrome React工具的时候
发现在输入框输入内容的时候,
这两个组件会一直重复渲染
- PopularSearch:搜索流行词组件
- SearchHistory:搜索历史组件
代码大致是下面这样的
class Search extends Component {
render() {
const {
inputText,
popularKeywords,
relatedKeywords,
historyKeywords
} = this.props
return (
<div>
<SearchBox
inputText={inputText}
onChange={this.handleChangeInput}
onClear={this.handleClearInput}
onCancel={this.handleCancelInput}/>
<PopularSearch
data={popularKeywords}
onClickKeyword={ this.handleClickKeyword } />
<SearchHistory
data={historyKeywords}
onClaerHistory={this.handleClearHistory} />
</div>
)
}
// ...
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
后来排查了一下原因
<SearchContainer>
<Search>
<SearchBox />
<PopularSearch />
<SearchHistory />
</Search>
</SearchContainer>
因为在输入框中输入了内容,导致更新了redux中inputValue的值
同时也引起我们生成的容器组件中state发生了变化,引起了整个<SearchContainer>
重绘,自然而然也引起了PopularSearch
和SearchHistory
的重绘
解决办法是在PopularSearch和SearchHistory中重写shouldComponentUpdate
然后其实还存在了一个问题,selector的重复计算。
稍后再来说。