fefe
fefe

Reputation: 9055

react with redux and react-router dispatches action twice

I just started to experiment with react and redux and I face couple of issues on the way.

When I try to render async data on route change the dispatched action is getting fired twice. First is undefined and than comes the real data.

Here is my store

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;

reducer

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;

actions

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'
    }
}

and here is my component

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

what do I wrong?

console

Upvotes: 1

Views: 1332

Answers (3)

Prakash Sharma
Prakash Sharma

Reputation: 16472

This is what happening here

  1. Your component render and show undefined on console because there is no profile data so far.
  2. After component mount it call componentDidmount which fire an action to fetch data from url.

  3. You get data from api and update the redux state which update your component as well.

  4. Therefore you render function is called again and this time it shows the profile data.

There is nothing dispatching two times. The code is perfectly fine.

Upvotes: 1

Lucas
Lucas

Reputation: 4097

You said //this.props.profile on first is undefined and then filled

That's because in the first render, state.profile is undefined, until the request response arrives and the setProfile action is dispatched.

There's also the problem Andrew noted that you're calling dispatch(doneFetchingProfile). Since you're using redux-thunk, that will trigger calling doneFetchingProfile(dispatch, getState), but the action HIDE_SPINNER will never get dispatched.


UPDATE: There's nothing wrong with your code. You can see before SHOW_SPINNER the output of console.log(this.props) and there's no profile because there's no profile in state as well.

Then when your request succeeds, profile is set in state, then passed to your component and then you can see in the log that profile is set. Those are not actions dispatched, this is the log of your props.

The first time is undefined because the initial state declared in the reducer is {} (there's no profile here as well).

If you change

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

to

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

you'll see that the first console.log(this.props) will show profile with the value 'some initial value' and then change to the remote data.

Upvotes: 1

Andrii Starusiev
Andrii Starusiev

Reputation: 7764

You dispatching 2 actions

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

First of them have no data, and it's look like tihs action set to state some data and update your component.

Upvotes: 0

Related Questions