Taylor Austin
Taylor Austin

Reputation: 5987

Getting error TypeError: Cannot read property 'id' of undefined using React/Redux action

I am using react/redux and the error is happening after a deleteRequest is called using an action. The reducer removes the item from the array and I think that is why this is happening, but I should be changing the location so it should not be rendering this page anymore.

The direct error is coming from the Post component down below from the this.props.deleteRecord in the catch block.

I am also using turbo which is how I make the deleteRequest and store the data. If you need a reference here is the docs. https://www.turbo360.co/docs

Reducer:

import constants from '../constants';

const initialState = {
  all: null
};

export default (state = initialState, action) => {
  switch (action.type) {
    case constants.POST_CREATED:
      return {
        ...state,
        all: state.all.concat(action.data),
        [action.data.id]: action.data
      };

    case constants.RECORD_UPDATED:
      return {
        ...state,
        [action.data.id]: action.data,
        all: all.map(item => (item.id === action.data.id ? action.data : item))
      };

    case constants.RECORD_DELETED:
      const newState = {
        ...state,
        all: state.all.filter(item => item.id !== action.data.id)
      };
      delete newState[action.payload.id];

      return newState;

    case constants.FETCH_POSTS:
      const sortedData = action.data.sort((a, b) => {
        return new Date(b.timestamp) - new Date(a.timestamp);
      });
      return { ...state, all: sortedData };

    case constants.FETCH_POST:
      return { ...state, [action.data.id]: action.data };

    default:
      return state;
  }
};

Component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import swal from 'sweetalert2/dist/sweetalert2.all.min.js';

import actions from '../../actions';
import { DateUtils } from '../../utils';
import { Reply } from '../containers';
import { UpdateRecord } from '../view';

class Post extends Component {
  constructor() {
    super();
    this.state = {
      editShow: false
    };
  }

  componentDidMount() {
    const { id } = this.props.match.params;
    if (this.props.posts[id] != null) {
      return;
    }
    this.props
      .getRecord(id)
      .then(() => {})
      .catch(err => {
        console.log(err);
      });
  }

  updateRecord(params) {
    const { id } = this.props.match.params;
    const post = this.props.posts[id];
    const { currentUser } = this.props.user;
    if (post.profile.id !== currentUser.id) {
      swal({
        title: 'Oops...',
        text: 'Must be owner of post',
        type: 'error'
      });
      return;
    }

    this.props
      .updateRecord(post, params)
      .then(response => {
        swal({
          title: 'Success',
          text: `${currentUser.username} Your post has been updated!`,
          type: 'success'
        });
      })
      .catch(err => {
        console.log(err);
      });
  }

  deleteRecord() {
    const { id } = this.props.match.params;
    const post = this.props.posts[id];
    const { currentUser } = this.props.user;

    if (currentUser.id !== post.profile.id) {
      swal({
        title: 'Oops...',
        text: 'Must be owner of post',
        type: 'error'
      });
      return;
    }

    this.props
      .deleteRecord(post)
      .then(() => {
        this.props.history.push('/');

        swal({
          title: 'Post Delete',
          text: 'Please create a new post',
          type: 'success'
        });
      })
      .catch(err => {
        console.log(err);
      });
  }

  render() {
    const { id } = this.props.match.params;
    const post = this.props.posts[id];
    const { currentUser } = this.props.user;
    if (post == null) {
      return <div />;
    }

    return (
      <div>
        <div className="jumbotron">
          <h1 className="display-3">{post.title}</h1>
          <div className="row" style={{ marginBottom: '25px' }}>
            <img className="img-fluid mx-auto" src={`${post.image}`} style={{ maxHeight: '400px' }} />
          </div>
          <p className="lead">{post.text}</p>
          <hr className="my-4" />
          {post.video == undefined ? null : (
            <div className="row justify-content-center">
              <div className="col-8">
                <div className="lead" style={{ marginBottom: '25px' }}>
                  <div className="embed-responsive embed-responsive-16by9">
                    <video style={{ background: 'black' }} width="800" controls loop tabIndex="0">
                      <source src={post.video} type={post.videoType} />
                      Your browser does not support HTML5 video.
                    </video>
                  </div>
                </div>
              </div>
            </div>
          )}
          <div className="lead">
            <Link to={`/profile/${post.profile.id}`}>
              <button className="btn btn-secondary btn-lg">{post.profile.username}</button>
            </Link>
            <p style={{ marginTop: '10px' }}>{DateUtils.relativeTime(post.timestamp)}</p>
          </div>
          {currentUser.id !== post.profile.id ? null : (
            <div className="row justify-content-end">
              <div className="col-md-2">
                <button
                  onClick={() => {
                    this.setState({ editShow: !this.state.editShow });
                  }}
                  className="btn btn-success"
                >
                  Edit
                </button>
              </div>
              <div className="col-md-2">
                <button onClick={this.deleteRecord.bind(this)} className="btn btn-danger">
                  Delete
                </button>
              </div>
            </div>
          )}
        </div>
        {this.state.editShow === false ? null : (
          <div>
            <UpdateRecord onCreate={this.updateRecord.bind(this)} currentRecord={post} />
          </div>
        )}
        <div>
          <Reply postId={post.id} />
        </div>
      </div>
    );
  }
}

const stateToProps = state => {
  return {
    posts: state.post,
    user: state.user
  };
};

const dispatchToProps = dispatch => {
  return {
    getRecord: id => dispatch(actions.getRecord(id)),
    updateRecord: (entity, params) => dispatch(actions.updateRecord(entity, params)),
    deleteRecord: entity => dispatch(actions.deleteRecord(entity))
  };
};

export default connect(stateToProps, dispatchToProps)(Post);

Upvotes: 2

Views: 3622

Answers (1)

Medet Tleukabiluly
Medet Tleukabiluly

Reputation: 11930

Take a look here

case constants.RECORD_DELETED:
  const newState = {
    ...state,
    all: state.all.filter(item => item.id !== action.data.id)
  };
  delete newState[action.payload.id];

  return newState;

You are using action.data when filtering, and action.payload when deleting from object

Upvotes: 2

Related Questions