Haris Khan Haris Khan -4 years ago 164
React JSX Question

Unable to update the state of my app, console.log inside reducer shows that object has changed, but the state remains same

Playing with redux has got me very confusing. Some how its getting so difficult to update the state of just one key in object.

A action is dispatched when user checks the checkbox inside of the component. When it is checked the status of the product should change from NEW to RESERVED:

// ProductRow.js

const ProductRow = React.createClass({
propTypes: {
item: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired
},

render () {
const { code, name, imageUrl, description } = this.props.item.product
const {status} = this.props.item
const {onChange} = this.props
return (
<div>
<label htmlFor={code}>
<input type='checkbox' id={code} onClick={onChange} value={status} />
<img src={imageUrl} />
<div className='name'>{name}</div>
<div className='description'>{description}</div>
</label>
</div>
)
}
})


The parent component of ProductRow is ProductList:

const ProductList = React.createClass({

propTypes: {
items: PropTypes.array.isRequired,
dispatchSetStatusToReservedForItem: PropTypes.func
},

handleProductCheckBoxClick (item) {
if (document.getElementById(item.product.code).checked) {
this.props.dispatchSetStatusToReservedForItem(item)
}
},

render () {
const {items} = this.props
return (
<div>
{
items.map((item) => {
return (
<ProductRow key={item.id} onChange={this.handleProductCheckBoxClick.bind(this, item)} item={item} />
)
})
}
</div>
)
}
})


When the checkbox is ticked i get the alerted by console.log inside of reducer and its shows the right output that is the right object inside of the array 'item' has changed, but somehow it is not reflected in the UI and rect developer tools.

//Reducer
const DEFAULT_STATE = {
shoppingCartData: shoppingCartData
}

const setStatusToReserved = (state, action) => {
const {items} = state.shoppingCartData.data

items.map(item => {
if(action.item.id === item.id) {
console.log(Object.assign({},action.item, {status: 'RESERVED'}))
return Object.assign({}, action.item, {status: 'RESERVED'})
}
})
}

const rootReducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {

case ACTIONS.SET_STATUS_TO_RESERVED:
return setStatusToReserved(state, action)

default:
return DEFAULT_STATE
}
}

export default rootReducer

Answer Source

If you change a part of your state, you need to create a new object reference for your state. With the way you're doing it, your old state will still === your new state, so there's no way for redux to know it changed.

Additionally, your setStatusToReserved function doesn't return anything at all!

Your state shouldn't be as deeply nested as it is, and I think there's more work to be done, but a step forward is something like:

const setStatusToReserved = (state, action) => {
  const {items} = state.shoppingCartData.data

  const newItems = items.map(item => {
    if(action.item.id === item.id) {
      console.log(Object.assign({},action.item, {status: 'RESERVED'}))
      return Object.assign({}, action.item, {status: 'RESERVED'})
    }
  })

  return {
    ...state,
    shoppingCartData: {
      ...shoppingCartData.data,
      items: newItems
    }
  }
}

I would highly suggest watching the Egghead Redux series which is free and addresses all of these problems.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download