Sachihiro
Sachihiro

Reputation: 1793

How to update the global state using redux and remove an item from the global state

I am trying to learn redux but I am stuck in this concept on how do i properly use the deleteCar that i have in actions. The reducer works well but the problem is at the global state of cars, how can I update the state of cars in Car component and then to call the deleteCar function and modify the state of cars. When i print the cars state in Car component, it is empty.

I have Component called Cars that is the parent component of Car (single car)

import React from "react";
import Car from "../Car";
import { connect } from "react-redux";

class Cars extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      cars: []
    };
  }
  componentDidMount() {
    fetch(url)
      .then(response => response.json())
      .then(data => this.setState({ cars: data }));
  }
  render() {
    const { cars } = this.state;
    return (
      <div>
        {cars.map(c => (
          <Car key={c.id} make={c.make} price={c.price} id={c.id} />
        ))}
      </div>
    );
  }
}

const mapStateToProps = reduxStoreState => {
  return {
    cars: reduxStoreState.cars
  };
};

export default connect(mapStateToProps)(Cars);

Single car

import React from "react";
import { deleteCar } from "../../actions/delete";
import { connect } from "react-redux";

const Car = ({ deleteCar, make, price, id }) => {
  return (
    <div className="card m-4">
      <div className="row">
        <div className="col">
          <h3>{make}</h3>
          <p>{price}</p>
        </div>
      </div>
      <div className="row">
        <button
          className="btn btn-danger"
          onClick={() =>
            deleteCar({
              id: id,
              make: make,
              price: price
            })
          }
        >
          Delete Car
        </button>
      </div>
    </div>
  );
};
const mapStateToProps = reduxStoreState => {
  return {
    cars: reduxStoreState.cars
  };
};

export default connect(mapStateToProps, { deleteCar })(Car);

Action

import { DELETE } from "../constants";
export const deleteCar = (payload) => {
  return {
    type: DELETE,
    payload: payload
  };
};

Reducer

import { ADD, DELETE, EDIT } from "../constants";

export default function(state = { cars: [] }, action) {
  switch (action.type) {
    case ADD:
      return {
        ...state,
        cars: [...state.cars, action.payload]
      };
    case DELETE:
      return {
        ...state,
        cars: state.cars.filter(obj => obj !== action.payload)
      };
    case EDIT:
      return {
        ...state,
        cars: action.payload
      };
    default:
      return state;
  }
}

Upvotes: 1

Views: 1869

Answers (1)

Brian Thompson
Brian Thompson

Reputation: 14395

The code below will not work as you expect:

cars: state.cars.filter(obj => obj !== action.payload)

In JavaScript, objects are stored in variables as references. By calling the delete action like this:

deleteCar({
  id: id,
  make: make,
  price: price
})

You are creating a new object each time, so they will never match and it will not be filtered.

It appears as if the comparison would be like this:

{ id: id, make: make, price: price } === { id: id, make: make, price: price }

But in reality, it is compared more like this:

// Both objects contain the same values
// But will not evaluate as equal because the references are different
'object_reference_1' === 'object_reference_2'

Instead I would recommend checking by the id like this:

cars: state.cars.filter(obj => obj.id !== action.payload.id)

The second problem is you have duplicated local and redux state in Cars.

Instead of using local state, dispatch an action to populate redux. It could look something like this:

componentDidMount() {
  fetch(url)
  .then(response => response.json())
  // Or preferably create a new action and call it like you did `deleteCar`
  .then(data => this.props.dispatch({ type: 'INITIALIZE', cars: data }));
}

Then in the reducer add this case:

switch (action.type) {
  case 'INITIALIZE':
    return {
      ...state,
      cars: action.payload
    };

Make sure you remove the local state for cars and replace const { cars } = this.state; to use this.props.

Upvotes: 3

Related Questions