0
votes

I am trying to get a single object from MongoDB. I am succeeding in getting the whole DB. When I console.log() the payload in the reducer, it shows a single object and not an array, and the correct object I wanted. I cannot understand why the object is not being sent to the component, since I am doing the exact same process in a different component.

From the action file: exerciseActions.js:

export const getExerciseByID = (exerciseID) => dispatch => {
    fetch('http://localhost:5000/exercises/' + exerciseID)
        .then(res => res.json())
        .then(exercise => dispatch({
            type: GET_BY_ID,
            payload: exercise
        }))
}

Reducers:

   import { FETCH_EXERCISES, NEW_EXERCISE, EXERCISE_REMOVE, GET_BY_ID } from '../actions/types'

const initialState = {
    exercises: [],
    exercise: {}
}

export default function (state = initialState, action) {
    switch (action.type) {
        case FETCH_EXERCISES:
            return {
                ...state,
                exercises: action.payload
            };
        case NEW_EXERCISE:
            return {
                ...state,
                exercise: action.payload
            };
        case EXERCISE_REMOVE:
            return {
                ...state,
                exercises: state.exercises.filter(exercises => exercises._id !== action.exercises._Id)
            };
        case GET_BY_ID:
            console.log(action.payload);
            return {
                exercise: action.payload
            }

        default: return state;
    }

}

react component:

import React, { Component } from 'react';
import axios from 'axios';
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css";
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getExerciseByID } from '../actions/exerciseActions'

class EditExercise extends Component {
    constructor(props) {
        super(props);

        this.onChangeUsername = this.onChangeUsername.bind(this);
        this.onChangeDescription = this.onChangeDescription.bind(this);
        this.onChangeDuration = this.onChangeDuration.bind(this);
        this.onChangeDate = this.onChangeDate.bind(this);
        this.onSubmit = this.onSubmit.bind(this);

        this.state = {
            exercise: null,
            users: []
        }
    }
    componentWillMount() {
        const exercise = this.props.getExerciseByID(this.props.match.params.id);
        console.log(exercise);
    }

    render() {

        const exercise = this.props.exercise

        return (
            <div>
                <p>{exercise}
                </p>
                <h3>Edit Exercise Log</h3>
                <form onSubmit={this.onSubmit}>
                    <div className="form-group">
                        <label>Username: </label>
                        <select ref="userInput"
                            required
                            className="form-control"
                            value={this.state.username}
                            onChange={this.onChangeUsername}>
                            {
                                this.state.users.map(function (user) {
                                    return <option
                                        key={user}
                                        value={user}>{user}
                                    </option>;
                                })
                            }
                        </select>
                    </div>
                    <div className="form-group">
                        <label>Description: </label>
                        <input type="text"
                            required
                            className="form-control"
                            value={this.state.description}
                            onChange={this.onChangeDescription}
                        />
                    </div>
                    <div className="form-group">
                        <label>Duration (in minutes): </label>
                        <input
                            type="text"
                            className="form-control"
                            value={this.state.duration}
                            onChange={this.onChangeDuration}
                        />
                    </div>
                    <div className="form-group">
                        <label>Date: </label>
                        <div>
                            <DatePicker
                                selected={this.state.date}
                                onChange={this.onChangeDate}
                            />
                        </div>
                    </div>

                    <div className="form-group">
                        <input type="submit" value="Edit Exercise Log" className="btn btn-primary" />
                    </div>
                </form>
            </div>
        )
    }
}

EditExercise.propTypes = {
    getExerciseByID: PropTypes.func.isRequired,
    exercise: PropTypes.array.isRequired,
}

const mapStateToProps = state => ({
    exercise: state.exercise,
})

export default connect(mapStateToProps, { getExerciseByID })(EditExercise);
1

1 Answers

1
votes

You might find the reason here. You should use componentDidMount.

Using a fetch call within componentWillMount() causes the component to render with empty data at first because componentWillMount() will NOT return before the first render of the component.

Due to the fact that JavaScript events are async, when you make an API call, the browser continues to do other work while the call is still in motion. With React, while a component is rendering it doesn’t wait for componentWillMount() to finish, so the component continues to render.

case GET_BY_ID:
    console.log(action.payload);
    return {
        ...state, // Btw, you forgot this line
        exercise: action.payload
    }