【问题标题】:React redux async mapStateToProps反应 redux 异步 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);

【问题讨论】:

    标签: javascript node.js reactjs redux react-redux


    【解决方案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) GetDoctorsGetTotalDoctors 被调用。

    3) 调用了一个新操作 isFetching,将您的状态保留为:

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

    4) MainAdmin 调用 render()

    5) 由于 state.doctors 为空且 state.isFetchingDoctorstrueMainAdmin.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 &amp;&amp; this.props.totalDoctorCount !== 0){return(){{doctorsList}}}else{return(){&lt;Loader&gt;&lt;/Loader&gt;}}
    • 差不多。 this: this.props.isFetc‌​hed() 只能在 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
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-01
    • 2019-01-18
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多