之前回复的时候对这个探讨理解不深,不过今天在重构一部分代码的时候回忆起了这个帖子,想分享下我现在的想法。不知道你们项目用的是什么,我的个人项目用的是Redux。
首先是我一些理解:
视图始终是先通过计算store中的数据,然后根据结果render。
View :: Model -> View
对应到React+Redux,再利用react-redux
中的connect,这个思路变的很清晰。
function mapStateToProps (state) {
return { ... }
}
@connect(mapStateToProps, {})
class Container extends Component {
render () {
...
}
}
通过mapStateToProps进行一些计算,再交给react组件来render。
然后回到实际情况:我的一个View依赖数据A,然后需要根据数据A的结果来请求数据B。对于呈现的内容而言,暂时没有请求到的数据用spinner占位或者就先空着。
由于View :: Model -> View
这是个同步的过程,那么现在的代码差不多是这样的:
function mapStateToProps (state, props) {
const cid = props.params.cid
const {
company: {
data: companyData, loadedCompany,
loadingCompany, loadFailedCompany
},
project: {
data: projectData,
companyProjectsLoading,
companyProjectsLoaded
}
} = state
// 数据A
const currentCompany = companyData[cid]
const needFetchCompany = !isDefined(currentCompany)
const companyFetching = has(loadingCompany, cid)
const companyFetchFailed = has(loadFailedCompany, cid)
// 数据B,它是依赖数据A的
const projectsUnderCompany = currentCompany &&
sortBy(currentCompany.projects.map(resolveProp(projectData)).filter(isDefined), 'name') || []
const needFetchProjectsUnderCompany = currentCompany &&
!has(companyProjectsLoading, cid) && !has(companyProjectsLoaded, cid) &&
currentCompany.projects.length > projectsUnderCompany
return ({
currentCompany, needFetchCompany,
companyFetching, companyFetchFailed,
projectsUnderCompany,
needFetchProjectsUnderCompany
})
}
由于整个模型是同步的,只能通过一些flag来表示加载状况,问题就是依赖关系稍稍复杂些就变的难以维护了。目前在考虑一个方案,想法是这样的:
- 对reducer维护的state的结构做一些约定,这样的话可以写一些util函数用来统一地判断数据是处于fetching还是loaded或者failed
- mapStateToProps计算的一部分结果可以抽象一个统一的Maybe类型(或者Either),交给View来呈现
- 我现在统一通过
needFetchProjectsUnderCompany
这种标志位来判断是否可以开始或者需要fetch数据,包括对之前结果有依赖的请求也是,而React组件中统一用一个ensureFetchData
方法来对这类标志位进行处理(即发送相应的请求),那么可能可以抽象出一个HOC来做这个事情,不过还在实践中。
这是我今天重新思考项目中一部分代码得出的结果。另外对目前简聊使用的方案表示好奇。