反应 redux 异步 mapStateToProps
Posted
技术标签:
【中文标题】反应 redux 异步 mapStateToProps【英文标题】:React redux async mapStateToProps 【发布时间】:2018-05-27 18:41:49 【问题描述】:怎么会在componentDidMount
中调用mapDispatchToProps
函数,
mapStateToProps
totalDoctorCount: state.doctors.totalDoctorCount
不会总是按时加载,我会得到undefined
结果console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );
。
我知道这是async
的本质,但是有没有办法解决它我在这里做错了什么。
完整代码:
医生操作
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");
)
医生减速器
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);
【问题讨论】:
【参考方案1】:有几种方法可以处理这个问题,但您必须首先了解如何处理影响您的 dom 的异步操作。
每当一个组件挂载时(取决于你如何设置你的应用程序,每当对 props、state 等进行更改时),它的渲染函数就会被调用。在您的示例中,组件安装,然后向服务器询问医生列表,调用render()
,然后然后从服务器接收医生列表。换句话说,当它调用 render 方法时,它还没有收到来自 axios 调用的医生列表。
如果您理解了所有这些,我们深表歉意。现在解释为什么this.props.totalDoctorCount
返回undefined
:直到函数getTotalDoctors
解析(即从服务器收到回复),您的应用程序的state.totalDoctorCount
才被定义。您可以通过在您的defaultState
中将totalDoctorCount
定义为0
(您将医生定义为一个空数组)来解决这个问题。
另一方面,您真的希望用户看到/认为总共有0
医生,直到服务器及时响应?这可能是考虑加载组件的好机会。我喜欢做的是在render()
的正下方,检查是否存在您需要遍历的任何列表,如果它为空,您可以返回一个LoadingComponent
(您可以自己制作并在任何地方使用它需要加载)。
这本身是不够的,因为您不希望页面在您实际上没有任何医生的情况下无限期加载,所以这个LoadingComponent
应该只在检索列表的函数出现它所关心的仍然是'fetching'。因此,也许您可以实现三个在获取之前、获取响应之后以及出现错误时调用的操作。
概述一下:
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) 您的服务器使用医生列表和totalDoctorCount
响应您的 axios 调用(注意:这将在不同时间发生,但为了简单起见,我将它们视为一起发生)。
7) 您的成功处理程序使用新的医生列表更新您的状态:
doctors: [1, 2, 3],
totalDoctors: 3,
isFetchingDoctors: true
8) MainAdmin calls
render()
再次因为状态的变化,但是因为state.isFetchingDoctors
仍然为真,它仍然会显示LoadingComponent。
8) 你的第二个新动作 isFetched()
被调用,你的状态为:
doctors: [1, 2, 3],
totalDoctors: 3,
isFetchingDoctors: false
9) MainAdmin
再次调用render()
,但这一次表明它不再加载的条件得到满足,您可以安全地遍历您的医生列表。
最后一点:你也可以在你 GetDoctors
时在你的 reducer 中将 isFetching 设置为 false,但我个人喜欢将异步状态函数分离到它们自己的函数中,以保持每个函数的座右铭只负责做一件事。
【讨论】:
感谢您提供的详细信息,如果我做对了,componentDidMount() 应该像这样componentDidMount()this.props.isFetching();this.props.getTotalDoctors();this.props.getDoctors(this.state.selected_filter);this.props.isFetched();
和 render() 应该像这样 render()if( this.props.isFetchingDoctors === false && this.props.totalDoctorCount !== 0)return()doctorsListelsereturn()<Loader></Loader>
差不多。 this: this.props.isFetched() 只能在 this.getTotalDoctors 解析后调用。因此,您可以将其作为回调传递给 this.getTotalDoctors: this.props.getTotalDoctors(function(response) /* Handle response */ this.props.isFetched(); 然后在 getTotalDoctors 内部调用回调(在 .然后())。
好像this.props.isFetched();没有被执行
是否需要更改医生操作才能进行回调
使用回调或承诺。如果您想要一个“isFetching”/“isFetched”状态属性,那么您需要确保在调用 isFetched() 之前 getTotalDoctors() 和 getDoctors() 都已解析。或者,您可以将 'isFetching'/'isFetched' 分离为 'isFetchingDoctors'/'isFetchingTotalDoctors' 等。应用程序中的某些组件可能不需要等待获得医生总数,但可能需要等待获得医生,等等。也就是说,这是一个我认为应该为你工作的快速而肮脏的例子:imgur.com/a/KZNkH以上是关于反应 redux 异步 mapStateToProps的主要内容,如果未能解决你的问题,请参考以下文章
我应该如何将“redux-thunk”用于异步初始状态? (反应/减少)