Reputation: 842
I am having server render on a demo react application of mine. Although it works if i refresh the page on a url to fetch the doctor like /doctor/:id if i am at /login and try to go to /doctor/123456 the doctor property is empty and (this.props.doctor.name.first) fails.
What is a good approach to use redux to fetch data on these cases?
Code is below
import { fetchDoctor } from '../../DoctorActions';
import { getDoctor } from '../../DoctorReducer';
class DoctorDetailPage extends Component {
render() {
return (
<div>{this.props.doctor.name.first}</div>
);
}
}
DoctorDetailPage.need = [params => {
return this.props.dispatch(fetchDoctor(params.id));
}];
function mapStateToProps(state, props) {
return {
doctor: getDoctor(state, props.params.id),
};
}
DoctorDetailPage.propTypes = {
doctor: PropTypes.shape({
insurance: PropTypes.string,
description: PropTypes.string,
GUID: PropTypes.string,
name: PropTypes.shape({
first: PropTypes.string,
last: PropTypes.string,
})
}),
dispatch: PropTypes.func.isRequired,
};
export default connect(mapStateToProps)(DoctorDetailPage);
REDUCER
import { ADD_DOCTOR } from './DoctorActions';
// Initial State
const initialState = { list: [] };
const DoctorReducer = (state = initialState, action = {}) => {
switch (action.type) {
case ADD_DOCTOR:
return {
list: [action.doctor, ...state.list],
};
default:
return state;
}
};
export const getDoctor = (state, id) => {
return state.doctors.list.filter(doctor => doctor._id === id)[0];
};
export default DoctorReducer;
ACTIONS
import callApi from '../../util/apiCaller';
// Export Constants
export const ADD_DOCTOR = 'ADD_DOCTOR';
// Export Actions
export function addDoctor(doctor) {
return {
type: ADD_DOCTOR,
doctor,
};
}
export function addDoctorRequest() {
return () => {
return true;
};
}
export function fetchDoctor(id) {
return (dispatch) => {
return callApi(`doctors/${id}`)
.then(res => dispatch(addDoctor(res)));
};
}
LOG ERROR
TypeError: Cannot read property 'name' of undefined
Upvotes: 2
Views: 870
Reputation: 6507
A user friendly approach would be to enter the page /doctor/123456
wihtout the need of having the doctor available, so user get's instant feedback that his action (navigate me to page x) worked. In onEnter
method of react-router or in componentDidMount
you should start an action fetchDoctor
and in a meanwhile show the user a spinner or a message indicating that the data is being loaded.
render() {
return (
<div>
{ this.props.doctor && <div>{this.props.doctor.name.first}</div> }
{ ! this.props.doctor && <YourSpinnerComponent/> }
</div>
);
}
So the above render method shows something while data is being loaded and when data comes in it displays it without any errors.
The "good old" way to handle async operations is to use redux-thunk
. You can read this great SO answer about dispatching asynchronous redux actions.
The latest trend is to use redux-saga
. It is a library that aims to make side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) in React/Redux applications easier and better. More about redux-saga
.
So in your case, you would create a Saga to handle the fetching.
More about redux-thunk
vs redux-saga
in this great SO answer.
Upvotes: 1