【问题标题】:react with redux and react-router dispatches action twicereact 与 redux 和 react-router 调度动作两次
【发布时间】:2017-02-22 18:41:10
【问题描述】:

我刚开始尝试使用 react 和 redux,途中遇到了几个问题。

当我尝试在路由更改时呈现异步数据时,调度的操作会被触发两次。首先是未定义,然后才是真正的数据。

这是我的商店

import { createStore, combineReducers, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import { routerReducer, routerMiddleware, push } from 'react-router-redux'
import reducers from '../reducers'
import { browserHistory } from 'react-router';

const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
    middleware.push(createLogger());
}

middleware.push(routerMiddleware(browserHistory));


    // Add the reducer to your store on the `routing` key
    const store = createStore(
        combineReducers({
            reducers,
            routing: routerReducer
        }),
        applyMiddleware(...middleware),

    )

    export default store;

减速器

export const RESOLVED_GET_PROFILE = 'RESOLVED_GET_PROFILE'

const profileReducer = (state = {}, action) => {
    switch (action.type) {
        case 'SET_PROFILE':
              return  {profile: action.profile}

        default:
            return state;
    }
};

export default profileReducer;

动作

import * as types from './actionTypes';
import Api from '../middleware/Api';

export function getProfile() {
    return dispatch => {
        dispatch(setLoadingProfileState()); // Show a loading spinner
        Api.get('profile').then(profile => {
            dispatch(doneFetchingProfile);
            dispatch(setProfile(profile));
        }).catch(error => {
            dispatch(showError(error));
            throw(error);
        });
    }
}

function setProfile(data) {
    return {
        type: types.SET_PROFILE,
        profile: data
    }
}


function setLoadingProfileState() {
    return {
        type: types.SHOW_SPINNER,
        loaded: false
    }
}

function doneFetchingProfile() {
    return {
        type: types.HIDE_SPINNER,
        loaded: true
    }
}

function showError() {
    return {
        type: types.SHOW_ERROR,
        loaded: false,
        error: 'error'
    }
}

这是我的组件

import React, {PropTypes, Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as profileActions from '../../../actions/profileActions';


class Profile extends Component {

    static propTypes = {
        profile: PropTypes.object.isRequired,
    };

    constructor(props) {
        super(props);

        this.state = {
            profile:{
                username: '',
                password: '',
                email: ''
            }
        }
        this.onUpdate = this.onUpdate.bind(this)
    }

    onUpdate(event) {
        alert()
    }

    componentDidMount() {
        //here I dispatch the action
        this.props.actions.getProfile()
    }

    componentWillReceiveProps(nextProps) {

    }

    render() {
        console.log(this.props)
        //this.props.profile on first is undefined and then filled
        const { profile } = this.props.profile

        return (
            <div>

            </div>
        );
    }
}

function mapStateToProps(state) {

    return {
        profile: state.default.profile,
    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(profileActions, dispatch)
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(Profile);

我做错了什么?

【问题讨论】:

    标签: reactjs action react-router react-redux dispatch


    【解决方案1】:

    你说//this.props.profile on first is undefined and then filled

    这是因为在第一次渲染中,state.profile undefined,直到请求响应到达并调度setProfile 操作。

    还有一个问题 Andrew 指出您正在调用 dispatch(doneFetchingProfile)。由于您使用的是 redux-thunk,这将触发调用 doneFetchingProfile(dispatch, getState),但操作 HIDE_SPINNER 将永远不会被调度。


    更新:您的代码没有问题。您可以在SHOW_SPINNER 之前看到console.log(this.props) 的输出,并且没有profile,因为状态中也没有profile

    然后当您的请求成功时,profile 被设置为状态,然后传递给您的组件,然后您可以在日志中看到profile 已设置。这些不是调度的动作,这是你的道具的日志。

    第一次是未定义的,因为在 reducer 中声明的初始状态是 {}(这里也没有 profile)。

    如果你改变了

    const profileReducer = (state = {}, action) => {
    

    const profileReducer = (state = {profile: 'some initial value'}, action) => {
    

    您会看到第一个console.log(this.props) 将显示profile,其值为'some initial value',然后更改为远程数据。

    【讨论】:

    • 所以我尝试了 dispatch(doneFetchingProfile(profile)) 并返回配置文件,但即使这样也是一样的。如何重构代码以使用数据触发一次?
    • @fefe 我不明白。您如何查看发送了哪些操作?根据代码SET_PROFILE 应该只发送一次。但是state.profile 假设有 2 个值:以undefined 开头,然后在调度上述操作后设置为正确的数据。
    • 我从控制台截屏
    • @fefe 我更新了我的评论。代码没有问题。这就是异步的工作原理。
    【解决方案2】:

    这就是这里发生的事情

    1. 您的组件在控制台上呈现并显示 undefined,因为目前还没有配置文件数据。
    2. 在组件挂载后,它调用 componentDidmount 触发一个动作从 url 获取数据。

    3. 您从 api 获取数据并更新 redux 状态,这也会更新您的组件。

    4. 因此您的渲染函数被再次调用,这次它显示配置文件数据

    没有什么分派两次。代码完全没问题。

    【讨论】:

      【解决方案3】:

      你调度了 2 个动作

      dispatch(doneFetchingProfile);
      dispatch(setProfile(profile));
      

      首先它们没有数据,看起来像是设置了动作来声明一些数据并更新您的组件。

      【讨论】:

      • 实际上,使用 redux-thunk 根本不会调度任何关于“设置配置文件”的操作
      猜你喜欢
      • 2018-02-21
      • 2019-08-24
      • 2021-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 2018-06-06
      相关资源
      最近更新 更多