Reputation: 9055
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?
Upvotes: 1
Views: 1332
Reputation: 16472
This is what happening here
After component mount it call componentDidmount which fire an action to fetch data from url.
You get data from api and update the redux state which update your component as well.
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
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
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