Reputation: 566
I am just playing around with Redux, so sorry for any mistakes or bad practices.
I basically have a list of computer parts which the user can select/deselect. Each part is a JS object which has id, name, price and selected field:
const initialState = {
products: [
{product_id: 0, product_name: "Hard Drive 320 GB 5400 RPM", price: 1450, selected: false},
{product_id: 1, product_name: "Solid State Drive 128 GB R/W - 420 MB/s", price: 1880, selected: false},
{product_id: 2, product_name: "Gigabyte MB A270S AM4 DDR4 3200HZ OC HDMI/VGA/DVI", price: 3560, selected: false},
{product_id: 3, product_name: "Asus MB A420M LGA 1151 3200HZ OC HDMI/VGA", price: 3660, selected: false},
{product_id: 4, product_name: "nVidia GTX 1050 Ti 4GB GDDR5 VRAM", price: 13450, selected: false},
{product_id: 5, product_name: "Intel i5 7200 kabylake CPU 3.6 GHz 4 cores 4 threads", price: 9800, selected: false},
{product_id: 6, product_name: "AMD Ryzed 5 2600G CPU 3.2 GHz 4 cores 4 threads", price: 9380, selected: false},
{product_id: 7, product_name: "8 GB Hyper-X DDR4 RAM memory 3200 MHz CL15", price: 3450, selected: false},
{product_id: 8, product_name: "450W Sharkoon PSU 80 plus black semi modular", price: 2250, selected: false},
{product_id: 9, product_name: "ATX Case w/ Acrilyc Glass panel & LED Strips", price: 1720, selected: false},
],
sum: 0
}
(this is just a practice so im not loading the data from a rest api or whatever)
Then, i subscribe to the redux store with my App component using the connect function, and i pass in my mapStateToProps and mapDispatchToProps functions:
function mapStateToProps(state){
return {
products: state.products,
sum: state.sum
}
}
function mapDispatchToProps(dispatch){
return {
toggle: (product_id) => dispatch(ACTIONS.toggle(product_id))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
Then inside of my app component, I create a const array to which i map all the products and generate a JSX return of a div with content inside it, and on each div i add an event linstener (onClick) which triggers the this.props.toggle dispatch:
const products = this.props.products.map(product => {
return (
<div className="product" key={product.product_id} onClick={ () => {this.props.toggle(product.product_id) }}>
<h2>{product.product_name}</h2>
{product.selected ? ('') : (<h3>{product.price}</h3>)}
</div>
)
})
And when the action is dispatched, this is what happens in the reducer:
switch(action.type){
case ACTION_TYPES.PRODUCT_TOGGLE:
const newState = state
newState.products[action.payload].selected = !newState.products[action.payload].selected
if(newState.products[action.payload].selected === true){
newState.sum = newState.sum + newState.products[action.payload].price
} else {
newState.sum = newState.sum - newState.products[action.payload].price
}
console.log(newState.sum)
return newState
default:
return state
}
If i do a console log in the reducer, i do see the change happening, but the props of the container component do not update.
Upvotes: 0
Views: 797
Reputation: 99
When you run newState = state you are just making a variable that's a reference to the same object as your old state, so as you assign new values to the object, you are mutating the old object.
Redux and React-Redux use strict equality checks to determine if your state has been updated. Since the object references are the same, it thinks that the state has not changed.
const oldState = { a: 1 };
const newState = oldState;
newState.a = 2;
console.log(oldState);
console.log(newState);
console.log(oldState === newState);
See this article on the subject of immutability: https://redux.js.org/faq/immutable-data#why-is-immutability-required-by-redux
Upvotes: 1
Reputation: 11760
You are mutating the state
object, newState
and state
are the same object reference that's why the UI
is not updating.
You have to return a new object from the reducer.
Create a new object before mutating the object like this
const newState = { ...state }
Upvotes: 2