I can't seem to figure out how to just load a component/object using the route parameters / ownProps my configuration is basically the same as is standard with the react redux apps and my actions/reducers work fine but for this component the data is not pre-loaded. Which I assume means there is a race happening where it is attempting to use the 'user' object before it is defined looks like. I assume this because it occasionally does work - it is going through the reducer(s) and api calls - and the result looks good. Is there some way I should first create a default 'user' object?
The route looks like
[tld]/user/1234
The component
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as userActions from '../../actions/userActions';
import User from './User';
class UserPage extends Component{
constructor(props, context){
super(props, context);
}
componentDidMount(){
this.fetchData();
}
fetchData(){
const {userId} = this.props;
const {fetchUser} = this.props.actions;
fetchUser(userId);
}
render(){
// This component when rendered has undefined property exceptions
// though sometimes it works :)
return(<User user={this.props.currentUser} />);
}
}
function mapStateToProps(state, ownProps){
return {
currentUser: state.user,
userId: ownProps.params.id
}
}
function mapDispatchToProps(dispatch){
return {
actions: bindActionCreators(userActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);
UPDATE So I came up with an ugly way to resolve this in the render
return (
{Object.keys(this.props.currentUser).length > 0 ?
<User user={this.props.currentUser} /> ? null }
)
This can't be the correct way to do this. - Also I notice if I just change the route id there is a brief flash of the previously loaded user while waiting for the delay of the api call to return the new user...Sorry react/redux are fairly new to me
there are ways to avoid undefined props:
1 Define initial state in user reducer
const initialState = { user:'', ... };
const user = (state = initialState, action) => {...};
export default user;
2 Handle undefined in mapStateToProps with state.user||""
const mapStateToProps = (state, ownProps) =>({
currentUser: state.user||"",
userId: ownProps.params.id
});
3 Sometimes necessary to not allow mounting component before data loaded.
const LoadingBlock = props => {
return (<div>{props.isLoading ? 'loading...' : props.children}</div>);
};
LoadingBlock.defaultProps = {
isLoading: true
};
///then wrap component that will be loading
<LoadingBlock isLoading={this.props.isLoading}>
<UserComponent user={this.props.user}>
</LoadingBlock>
4 if this.props.user undefined, then not show userComponent
render(){
let {user} = this.props
return(
<div>{user && <UserComponent user={user}>}</div>
)
}
5 if this.props.user is not undefined, then show component, else show some "content"
render(){
let {user} = this.props
return(
<div>{user ? <UserComponent user={user}> : "loading"}</div>
)
}
6 define default props
class Control extends React.Component {...}
Control.defaultProps = {value: "somevalue"};
Couple of things I can think of:
Checking if this.props.currentUser is defined is actually a good idea, but I would use this syntax:
const { currentUser } = this.props;
return currentUser && <User user={currentUser} />;
componentDidMount is only called when the component is added to the DOM. So if the UserPage component is already in the DOM, it wont trigger and your fetch code will never be called. In that case you could use the componentWillReceiveProps event to evaluate the new props. (see: https://facebook.github.io/react/docs/component-specs.html for more info)
What does fetchUser do exactly? I assume its an async call to fetch the user data. Are you using some middleware like redux-thunk? How does that code look?
If you're using Thunk, you can dispatch multiple actions to indicate the status of the fetch. So, something like this:
export function fetchUser(userId) {
return dispatch => {
dispatch(fetchUserStart());
someService.fetchUser(userId)
.then(user => dispatch(fetchUserDone(user)))
.catch(error => {
dispatch(fetchUserError(error));
});
};
}
In the above code fetchUserStart, fetchUserDone and fetchUserError are all actions (code not shown here) which are affecting the user state. fetchUserStart could set the user state to undefined, whereas fetchUserDone sets it to the user you got from the async fetch. As your UserPage component is connected to this state, it will react by showing / not showing the User component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With