Jonas Lu
Jonas Lu

Reputation: 181

cannot read property 'id' of undefined in ReactJS

i am currently React SPA application based on react-redux. in the reducer.js, I have two main states, which is mylist and recommendations. I have attached two picture of actually applicaion. there are two pages. first one is Home page which there are three initial recommendations of movies. under each movie there is a add button, which once you clicked the botton and the expectation is selected movie will be removed and will be added to second pages which is a mylist page.

on the mylist page, there will be three initial movie. there is a remove button underneath each movie, once i have clicked remove button, expectation is the selected movie is removed and will be added back to home page.

rightnow, i have implemented the add button, which once i clicked add button, the selected movie will be removed and will be add to mylist page.

the problem is in the mylist page once i clicked remove. the selected movie is removed but its not added to the home page.

Home.js

class Home extends Component {
  handleClick = id => {
    this.props.addToList(id);
    this.props.removeFromRecom(id);
  };
  render() {
    let addedRecom1 = this.props.addedRecom.map(item => {
      return (
        <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
          <Image webP={item.img}></Image>
          <p style={{ marginLeft: "44px" }}>{item.title}</p>
          <button
            to="/"
            onClick={() => this.handleClick(item.id)}
            style={{
              marginLeft: "53px",
              paddingLeft: "10px",
              paddingRight: "10px",
              paddingTop: "0px",
              marginTop: "0px",
              marginBottom: "20px"
            }}
          >
            add
          </button>
        </div>
      );
    });
    return (
      <div>
        <div>
          <NavLink to="/myList" style={{ textDecoration: "none" }}>
            <a
              style={{
                marginLeft: "378px",
                fontFamily: "Arial Black",
                color: "black",
                border: "solid 1px",
                backgroundColor: "orange",
                paddingLeft: "35px",
                paddingRight: "20px"
              }}
            >
              <i
                class="fa fa-plus"
                style={{ paddingRight: "10px", paddingLeft: "5px" }}
              ></i>
              My list
            </a>
          </NavLink>
          <p style={{ marginTop: "20px", marginLeft: "20px" }}>
            Recommendations
          </p>
        </div>
        <div>
          {this.props.recommendations.map(item => {
            return (
              <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
                <Image webP={item.img}></Image>
                <p style={{ marginLeft: "44px" }}>{item.title}</p>
                <button
                  to="/"
                  onClick={() => this.handleClick(item.id)}
                  style={{
                    marginLeft: "53px",
                    paddingLeft: "10px",
                    paddingRight: "10px",
                    paddingTop: "0px",
                    marginTop: "0px",
                    marginBottom: "20px"
                  }}
                >
                  add
                </button>
              </div>
            );
          })}
        </div>
        {addedRecom1}
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    addedRecom: state.addedRecom,
    recommendations: state.recommendations
  };
};

const mapDispatchToProps = dispatch => {
  return {
    addToList: id => {
      dispatch(addToList(id));
    },
    removeFromRecom: id => {
      dispatch(removeFromRecom(id));
    }
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Home);

MyList.js

class MyList extends Component {
  handleRemoveMovie = id => {
    this.props.removeFromList(id);
    this.props.addToRecom(id);
  };
  render() {
    let addedMovies = this.props.addedMovies.map(item => {
      return (
        <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
          <Image webP={item.img}></Image>
          <p style={{ marginLeft: "44px" }}>{item.title}</p>
          <button
            to="myList"
            onClick={() => {
              this.handleRemoveMovie(item.id);
            }}
            style={{
              marginLeft: "53px",
              paddingLeft: "10px",
              paddingRight: "10px",
              paddingTop: "0px",
              marginTop: "0px",
              marginBottom: "20px"
            }}
          >
            remove
          </button>
        </div>
      );
    });
    return (
      <div>
        <div>
          <NavLink to="/" style={{ textDecoration: "none" }}>
            <a
              style={{
                marginLeft: "317px",
                fontFamily: "Arial Black",
                color: "black",
                border: "solid 1px",
                backgroundColor: "orange",
                paddingLeft: "28px",
                paddingRight: "20px"
              }}
            >
              <i
                class="fa fa-hand-o-left"
                style={{ paddingRight: "10px", paddingLeft: "5px" }}
              ></i>
              back to Home
            </a>
          </NavLink>
          <p style={{ marginTop: "20px", marginLeft: "20px" }}>My Lists</p>
        </div>
        <div>
          {this.props.mylist.map(item => {
            return (
              <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
                <Image webP={item.img}></Image>
                <p style={{ marginLeft: "44px" }}>{item.title}</p>
                <button
                  to="myList"
                  onClick={() => {
                    this.handleRemoveMovie(item.id);
                  }}
                  style={{
                    marginLeft: "53px",
                    paddingLeft: "10px",
                    paddingRight: "10px",
                    paddingTop: "0px",
                    marginTop: "0px",
                    marginBottom: "20px"
                  }}
                >
                  remove
                </button>
              </div>
            );
          })}
        </div>
        {addedMovies}
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    addedMovies: state.addedMovies,
    mylist: state.mylist
  };
};

const mapDispatchToProps = dispatch => {
  return {
    removeFromList: id => {
      dispatch(removeFromList(id));
    },
    addToRecom: id => {
      dispatch(addToRecom(id));
    }
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(MyList);

reducer.js

const initialState = {
  mylist: [
    {
      title: "Futurama",
      id: 1,
      img: "http://cdn1.nflximg.net/webp/7621/3787621.webp"
    },
    {
      title: "The Interview",
      id: 2,
      img: "http://cdn1.nflximg.net/webp/1381/11971381.webp"
    },
    {
      title: "Gilmore Girls",
      id: 3,
      img: "http://cdn1.nflximg.net/webp/7451/11317451.webp"
    }
  ],
  recommendations: [
    {
      title: "Family Guy",
      id: 4,
      img: "http://cdn5.nflximg.net/webp/5815/2515815.webp"
    },
    {
      title: "The Croods",
      id: 5,
      img: "http://cdn3.nflximg.net/webp/2353/3862353.webp"
    },
    {
      title: "Friends",
      id: 6,
      img: "http://cdn0.nflximg.net/webp/3200/9163200.webp"
    }
  ],
  addedMovies: [],
  addedRecom: []
};

const reducer = (state = initialState, action) => {
  if (action.type === REMOVE_FROM_RECOM) {
    let Recom = state.recommendations.filter(item => item.id !== action.id);
    return {
      ...state,
      recommendations: Recom
    };
  }

  if (action.type === ADD_TO_LIST) {
    let addedMovie = state.recommendations.find(item => item.id === action.id);
    let existed_movie = state.mylist;
    if (existed_movie) {
      return {
        ...state,
        addedMovies: [...state.addedMovies, addedMovie]
      };
    }
  }

  if (action.type === ADD_TO_RECOM) {
    let add_recom = state.mylist.find(item => item.id === action.id);
    let current_recom = state.recommendations;
    if (current_recom) {
      return {
        ...state,
        addedRecom: [...state.addedRecom, add_recom]
      };
    }
  }

  if (action.type === REMOVE_FROM_LIST) {
    let removedMovie = state.mylist.find(item => item.id === action.id);
    let newMovie = state.addedMovies.filter(item => item.id !== action.id);
    return {
      ...state,
      addedMovies: newMovie
    };
  } else {
    return state;
  }
};[![enter image description here][1]][1]

export default reducer;


TypeError: Cannot read property 'id' of undefined
(anonymous function)
src/Home.js:17

  14 | render() {
  15 |   let addedRecom1 = this.props.addedRecom.map(item => {
  16 |     return (
> 17 |       <div key={item.id} style={{ float: "left", marginLeft: "20px" }}>
     | ^  18 |         <Image webP={item.img}></Image>
  19 |         <p style={{ marginLeft: "44px" }}>{item.title}</p>
  20 |         <button

[![enter image description here][1]][1] enter image description here

Upvotes: 0

Views: 1382

Answers (1)

Tomer
Tomer

Reputation: 1568

In general you should have only two lists, one for recommendations and one for myList, and two reducers that handle deletion and addition of items from myList:

const initialState = {
    myList: [
        {
            title: 'Futurama',
            id: 1,
            img: 'http://cdn1.nflximg.net/webp/7621/3787621.webp'
        },
        {
            title: 'The Interview',
            id: 2,
            img: 'http://cdn1.nflximg.net/webp/1381/11971381.webp'
        },
        {
            title: 'Gilmore Girls',
            id: 3,
            img: 'http://cdn1.nflximg.net/webp/7451/11317451.webp'
        }
    ],
    recommendations: [
        {
            title: 'Family Guy',
            id: 4,
            img: 'http://cdn5.nflximg.net/webp/5815/2515815.webp'
        },
        {
            title: 'The Croods',
            id: 5,
            img: 'http://cdn3.nflximg.net/webp/2353/3862353.webp'
        },
        {
            title: 'Friends',
            id: 6,
            img: 'http://cdn0.nflximg.net/webp/3200/9163200.webp'
        }
    ]
};

Each reducer is incharge of updating both recommendations and myList upon deletion/addition of a movie:

const reducer = (state = initialState, action) => {
    if (action.type === 'ADD_TO_LIST') {
        let addToMyList = state.recommendations.find(item => item.id === action.id);
        return {
            ...state,
            myList: [ ...state.myList, addToMyList ],
            recommendations: state.recommendations.filter(item => item.id !== action.id)
        };
    }

    if (action.type === 'REMOVE_FROM_LIST') {
        let removedItem = state.myList.find(item => item.id === action.id);
        return {
            ...state,
            myList: state.myList.filter(item => item.id !== action.id),
            recommendations: [ ...state.recommendations, removedItem ]
        };
    } else {
        return state;
    }
};

export default reducer;

Upvotes: 1

Related Questions