Panagiotis Vrs
Panagiotis Vrs

Reputation: 842

React Redux server render but client side fetch data

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

Answers (1)

Deividas
Deividas

Reputation: 6507

What is a good approach to fetch data in general?

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.

What is a good approach to fetch data using redux?

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

Related Questions