Reputation: 1793
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
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