How e when calling mapDispatchToProps
functions in ponentDidMount
,
mapStateToProps
totalDoctorCount: state.doctors.totalDoctorCount
wont always load on time and I would get undefined
result in console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );
.
I know it's the nature of async
, but is there a way to fix it am i doing something wrong here.
Full Code :
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;
}
server 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);
});
});
ponent
class MainAdmin extends React.Component{
constructor(){
super();
this.state = {
selected_filter:"dateCreated"
};
}
openAddDoctorModal = () => {
this.setState({AddDoctorModal:true});
}
closeAddDoctorModal = () => {
this.setState({AddDoctorModal:false});
}
ponentDidMount(){
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);
How e when calling mapDispatchToProps
functions in ponentDidMount
,
mapStateToProps
totalDoctorCount: state.doctors.totalDoctorCount
wont always load on time and I would get undefined
result in console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );
.
I know it's the nature of async
, but is there a way to fix it am i doing something wrong here.
Full Code :
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;
}
server 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);
});
});
ponent
class MainAdmin extends React.Component{
constructor(){
super();
this.state = {
selected_filter:"dateCreated"
};
}
openAddDoctorModal = () => {
this.setState({AddDoctorModal:true});
}
closeAddDoctorModal = () => {
this.setState({AddDoctorModal:false});
}
ponentDidMount(){
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);
Share
Improve this question
edited Dec 13, 2017 at 22:13
codejockie
10.9k4 gold badges49 silver badges57 bronze badges
asked Dec 13, 2017 at 19:04
publicArt33publicArt33
3159 silver badges16 bronze badges
1 Answer
Reset to default 2There are several ways you can handle this, but you must first understand how to handle asynchronous actions that affect your dom.
Whenever a ponent mounts (and depending on how you've set your app up, whenever a change is made to props, state, etc), its render function is called. In your example, the ponent mounts, then asks the server for the list of doctors, calls render()
, and then receives the list of doctors from the server. To rephrase, by the time it called the render method, it has not yet received the list of doctors from the axios call.
Apologies if you understood all of this. Now for why this.props.totalDoctorCount
is returning undefined
: your app's state.totalDoctorCount
is not being defined until the function getTotalDoctors
resolves (i.e., hears back from server). You can fix this rather simply by defining totalDoctorCount
as 0
in your defaultState
(where you defined doctors as an empty array).
On the other hand, do you really want users to see/think that there are a total of 0
doctors until the server responds in time? This might be a good opportunity to consider a loading ponent. What I like to do is right below render()
, check for the existence of whatever list you need to iterate through and if it is empty, you can return a LoadingComponent
(you can make this on your own and use it wherever something needs to load).
This by itself is not enough, because you don't want the page to load indefinitely in the event that you actually don't have any doctors, so this LoadingComponent
should only appear if the function that is retrieving the list that it's concerned with is still 'fetching'. So perhaps you can implement three actions that are called before you fetch, after the fetch is responded to, and if there is an error.
So to outline:
1) MainAdmin mounts.
2) GetDoctors
and GetTotalDoctors
are called.
3) A new action isFetching
is called, leaving your state as:
{
doctors: [],
totalDoctors: 0, //assuming you have added this to defaultState
isFetchingDoctors: true
}
4) MainAdmin
calls render()
.
5) since state.doctors is empty and state.isFetchingDoctors
is true
, MainAdmin.render()
returns your new LoadingComponent
.
6) Your server responds to your axios call with the list of doctors and the totalDoctorCount
(note: this will happen at different times, but for sake of simplicity, I am treating them as happening together).
7) Your success handler updates your state with the new list of doctors:
{
doctors: [1, 2, 3],
totalDoctors: 3,
isFetchingDoctors: true
}
8) MainAdmin calls
render()
again because of the change in state, but because state.isFetchingDoctors
is still true, it will still show LoadingComponent.
8) Your second new action isFetched()
is called, leaving your state as:
{
doctors: [1, 2, 3],
totalDoctors: 3,
isFetchingDoctors: false
}
9) MainAdmin
calls render()
again, but this time the conditions to indicate that it is no longer loading are met, and you can safely iterate through your list of doctors.
One final note: You could also set isFetching to false in your reducer as soon as you GetDoctors
but I personally like to separate the asynchronous status functions into their own function to keep to the motto of every function only tasked with doing one thing.