反应reduxasynchronousmapStateToProps

当在componentDidMountmapStateToProps调用mapDispatchToProps函数的时候, mapStateToProps totalDoctorCount: state.doctors.totalDoctorCount不会总是按时加载,我会在console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );得到undefined结果console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );
我知道这是async的本质,但有没有办法解决这个问题,我在这里做错了什么。

完整代码:

doctorActions

 export function getDoctors(filterType){ return function(dispatch){ axios.get("/api/doctors/"+filterType) .then(function(response){ dispatch({type:"GET_DOCTORS",payload:response.data}); }) .catch(function(err){ dispatch({type:"GET_DOCTORS_REJECTED",payload:err}); }) } } export function getTotalDoctors(){ return function(dispatch){ axios.get("/api/getTotalDoctors/") .then(function(response){ dispatch({type:"TOTAL_DOCTORS",payload:response.data}); console.log(response.data); }) .catch(function(err){ //console.log(err); dispatch({type:"TOTAL_DOCTORS_REJECTED",payload:"there was an error rortal doctors"}); }) } } 

doctorReducer

 export function doctorsReducers(state={ doctors:[], }, action){ switch(action.type){ case "GET_DOCTORS": // return the state and copy of boos array from state return {...state,doctors:[...action.payload]} break; case "TOTAL_DOCTORS": // return the state and copy of boos array from state return { ...state, totalDoctorCount:action.payload } break; } return state; } 

服务器API

 app.get('/doctors/:filterType',function(req,res){ let filterType = req.params.filterType; var query = {}; if(filterType == "dateCreated"){ query = {date_created: 'desc'}; }else if(filterType == "dateUpdated"){ query = {date_updated: 'desc'}; } Doctors.find({}).sort(query).limit(3).exec(function(err,doctors){ if(err){ throw err; } res.json(doctors); }); }); app.get('/getTotalDoctors',function(req,res){ Doctors.count({}, function(err, count){ if(err){ throw err; } res.json(count); }); }); 

零件

 class MainAdmin extends React.Component{ constructor(){ super(); this.state = { selected_filter:"dateCreated" }; } openAddDoctorModal = () => { this.setState({AddDoctorModal:true}); } closeAddDoctorModal = () => { this.setState({AddDoctorModal:false}); } componentDidMount(){ this.props.getTotalDoctors(); this.props.getDoctors(this.state.selected_filter); } loadPage = (pageNum) => { //alert(pageNum); this.props.loadPage(pageNum,this.state.selected_filter); } render(){ const doctorsList = this.props.doctors.map(function(doctorsArr){ return( <Col xs={12} sm={12} md={12} key={doctorsArr._id}> <DoctorsItem _id = {doctorsArr._id} doc_fname = {doctorsArr.doc_fname} doc_lname = {doctorsArr.doc_lname} /> </Col> ) }); //const lengthPage = parseInt(this.props.totalDoctorCount/3); console.log("this.props.totalDoctorCount2: "+this.props.totalDoctorCount ); const pages = parseInt(this.props.totalDoctorCount/3, 10); console.log("pages: "+pages ); const pageNums = [...Array(pages)].map((pageNum, i) => { return( <Col xs={2} sm={2} md={2} key={i+1}> <Button onClick={() => this.loadPage(i+1)} bsStyle="success" bsSize="small"> {i+1} </Button> </Col> ) }); return( <Well> <Row style={{marginTop:'15px'}}> {doctorsList} </Row> <Row style={{marginTop:'15px'}}> {pageNums} </Row> </Well> ) } } function mapStateToProps(state){ return{ doctors: state.doctors.doctors, totalDoctorCount:state.doctors.totalDoctorCount } } function mapDispatchToProps(dispatch){ return bindActionCreators({ getDoctors:getDoctors, loadPage:loadPage, getTotalDoctors:getTotalDoctors },dispatch) } export default connect(mapStateToProps,mapDispatchToProps)(MainAdmin); 

有几种方法可以处理这个问题,但是您必须先了解如何处理影响您的dom的asynchronous操作。 每当一个组件挂载(并且取决于你如何设置你的应用程序,每当对props,state等进行更改时),就会调用它的render函数。 在你的例子中,组件挂载,向服务器请求医生列表,调用render(), 然后从服务器接收医生列表。 换句话说,在它叫做渲染方法的时候,它还没有收到axios调用的医生名单。

道歉,如果你明白这一切。 现在为什么this.props.totalDoctorCount返回undefined:你的应用程序的state.totalDoctorCount直到函数getTotalDoctorsparsing(即从服务器回听)才被定义。 你可以通过在你的defaultState中定义totalDoctorCount为0(在这里你将医生定义为一个空数组)来解决这个问题。

另一方面,你真的希望用户看到/认为总共有0位医生,直到服务器及时响应? 这可能是考虑加载组件的好机会。 我喜欢做的就是在render()下面,检查你需要迭代的列表是否存在,如果它是空的,你可以返回一个LoadingComponent(你可以自己做,并在任何地方使用它需要加载)。 这本身是不够的,因为你不希望页面无限期地加载,如果你实际上没有任何医生,所以这个LoadingComponent应该只在检索它所关心的列表的函数是还在“取材”。 所以也许你可以实现三个在你获取之前被调用的动作,在获取响应之后,以及是否有错误。

所以概述:

1)MainAdmin坐骑。

2)GetDoctors和GetTotalDoctors被调用。

3)调用一个新的动作“isFetching”,把你的状态保持为:

 { doctors: [], totalDoctors: 0, //assuming you have added this to defaultState isFetchingDoctors: true } 

4)MainAdmin调用render()。

5)因为state.doctors是空的,state.isFetchingDoctors是true,MainAdmin.render()返回你的新的LoadingComponent。

6)你的服务器响应你的axios呼叫的医生名单和totalDoctorCount(注意:这将发生在不同的时间,但为了简单起见,我把他们当作一起发生)。

7)你的成功经理用新的医生名单更新你的状态:

 { doctors: [1, 2, 3], totalDoctors: 3, isFetchingDoctors: true } 

8)MainAdmin再次调用render()是因为状态的改变,但是因为state.isFetchingDoctors仍然是true,它仍然会显示LoadingComponent。

8)你的第二个新动作是被调用(),将你的状态保持为:

 { doctors: [1, 2, 3], totalDoctors: 3, isFetchingDoctors: false } 

9)MainAdmin再次调用render(),但是这一次满足了表示不再加载的条件,并且可以安全地遍历你的医生列表。

最后一个注意事项:你可以在GetDoctors中尽可能的设置isFetching为false,但是我个人喜欢把asynchronous状态函数分离到自己的函数中,以保持每一个只负责做一件事情的函数的座右铭。