简体   繁体   English

React redux async mapStateToProps

[英]React redux async mapStateToProps

How come when calling mapDispatchToProps functions in componentDidMount , 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 ); 为什么在componentDidMount调用mapDispatchToProps函数时, mapStateToProps totalDoctorCount: state.doctors.totalDoctorCount不会总是按时加载,我会在console.log("this.props.totalDoctorCount: "+this.props.totalDoctorCount );得到undefined结果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. 我知道这是async的本质,但有没有办法解决它我在这里做错了什么。

Full Code : 完整代码:

doctorActions 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 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 服务器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);
});
});

component 零件

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);

There are several ways you can handle this, but you must first understand how to handle asynchronous actions that affect your dom. 有几种方法可以解决这个问题,但您必须首先了解如何处理影响dom的异步操作。

Whenever a component 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. 每当组件安装(并且取决于您如何设置应用程序,每当对props,state等进行更改)时,都会调用其render函数。 In your example, the component mounts, then asks the server for the list of doctors, calls render() , and then receives the list of doctors from the server. 在您的示例中,组件安装,然后向服务器请求医生列表,调用render()然后从服务器接收医生列表。 To rephrase, by the time it called the render method, it has not yet received the list of doctors from the axios call. 换句话说,当它调用render方法时,它还没有从axios调用中收到医生列表。

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 (ie, hears back from server). 现在为什么this.props.totalDoctorCount返回undefined :你的应用程序的state.totalDoctorCount在函数getTotalDoctors结算之前没有被定义(即,从服务器回来)。 You can fix this rather simply by defining totalDoctorCount as 0 in your defaultState (where you defined doctors as an empty array). 您可以通过在defaultState totalDoctorCount定义为0 (您将医生定义为空数组)来简单地修复此问题。

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? 另一方面,你真的希望用户看到/认为总共有0名医生,直到服务器及时响应? This might be a good opportunity to consider a loading component. 这可能是考虑加载组件的好机会。 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). 我喜欢做的是在render()下面,检查是否存在需要迭代的列表,如果它是空的,你可以返回一个LoadingComponent (你可以自己做这个并在需要的地方使用它)加载)。

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' . 这本身是不够的,因为您不希望页面在您实际上没有任何医生的情况下无限期加载,因此只有在检索LoadingComponent的列表的函数时才会出现此LoadingComponent还在'取材' 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. 1)MainAdmin安装。

2) GetDoctors and GetTotalDoctors are called. 2) GetDoctorsGetTotalDoctors

3) A new action isFetching is called, leaving your state as: 3)调用一个新动作isFetching ,将你的状态保持为:

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

4) MainAdmin calls render() . 4) MainAdmin调用render()

5) since state.doctors is empty and state.isFetchingDoctors is true , MainAdmin.render() returns your new LoadingComponent . 5)由于state.doctors为空且state.isFetchingDoctorstrue ,因此MainAdmin.render()返回新的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). 6)您的服务器使用医生列表和totalDoctorCount响应您的axios呼叫(注意:这将在不同时间发生,但为了简单起见,我将它们视为一起发生)。

7) Your success handler updates your state with the new list of doctors: 7)您的成功处理程序使用新的医生列表更新您的州:

{
    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)由于状态的改变, MainAdmin calls再次MainAdmin calls render() ,但由于state.isFetchingDoctors仍为true,它仍将显示LoadingComponent。

8) Your second new action isFetched() is called, leaving your state as: 8)你的第二个新动作isFetched()被调用,你的状态为:

{
    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. 9) MainAdmin再次调用render() ,但是这次表示它不再加载的条件得到满足,你可以安全地遍历你的医生列表。

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. 最后一点:你也可以尽快设置isFetching为false你减速,你GetDoctors但我个人喜欢异步状态功能分离到自己的功能,以保持各功能的座右铭只有做一件事情负责。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM