Lukas Luke Stateczny
Lukas Luke Stateczny

Reputation: 597

React Passing Props from parent to child behaves unexpectedly

Here is an example of Editing Fields for Product's sizes.

Product Sizes

I'm able to edit the size and amount and add the new size and amount. Problem comes with deleting it. When I delete it from bottom everything works as expected:

Deletion Successfull

However when I want to delete from the top or even from the middle instead it deletes the bottom one.

Here is the Parent of the Edit Component:

constructor(props){
   super(props)
   this.state={
      ...

      size: {}
   }
}

handleDeleteSize(obj, prevSize){
    var modSizes = {}
    delete obj[prevSize]
    var newSizes = Object.assign(obj, modSizes)
    this.setState({
        size: newSizes
    })
}

...

<Paper>
   ...

      <EditSizes _sizes={this.state.size} _handleDeleteSize={this.handleDeleteSize.bind(this)}></EditSizes>

</Paper>

Among other props and functions I am passing the state's size object that holds sizes and the amount of products.

Here is the EditSizes Component:

import React from 'react'

// Edit Size
import EditSize from './Edit Size/editsize'

...

class EditSizes extends React.Component {
   constructor(props){
      super(props)
         this.state={
            sizes: {},
      }
   }

   componentDidMount(){
      this.setState({
         sizes: this.props._sizes,
         handleDeleteSize: this.props._handleDeleteSize,

         ...
      })
   }

   render(){
      return(
         <ul>
            {Object.keys(this.state.sizes).map((item, index)=>{
                return <EditSize key={index} _handleDeleteSize={this.state._handleDeleteSize.bind(this)} _otherSizes={this.state.sizes} _size={item} _amount={this.state.sizes[item]}></EditSize>
            })}

            ...
         </ul>
      )
   }
}

export default EditSizes;

This is the place It makes no sense to me. When I console log each item from the this.state.sizes it console logs it correctly. So for example if my sizes are L, M, XL it the console prints just that: L, M, XL. But when I pass item to _size prop in EditSize component, for some reason it retains the previous size.

For example if I want to delete L size. The console will throw something like this in EditSize component:

Console Log

In _otherSizes field I can see the sizes that are remaining after size L has been deleted, but in _size field instead of seeing M, the component for some reason retains the old one. (I use _size as an indicator for which size should be deleted)

Here is the EditSize - child component of EditSizes:

import React from 'react';

// Material UI
import { IconButton, TextField, Paper } from '@material-ui/core';

// Material Icons
import DeleteIcon from '@material-ui/icons/Delete';

// React Bootstrap
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

class EditSize extends React.Component {
   constructor(props){
      super(props)
         this.state = {
            size: '',
            otherSizes: {},
            amount: '',
            barcode: '',
         }

         ...

         this.handleDelete = this.handleDelete.bind(this)
   }

   componentDidMount(){
      this.setState({
         prevSize: this.props._size,
         size: this.props._size,
         otherSizes: this.props._otherSizes,
         amount: this.props._amount[0],
         barcode: this.props._amount[1]
      })
   }

   componentDidUpdate(prevProps, prevState, snapshot){
      //The Console message comes from here
      console.log(prevProps)
   }

   handleDelete(){
      var prevSize = Object.keys(this.state.otherSizes).find((item)=>item === this.state.size)
      this.props._handleDeleteSize(this.state.otherSizes, prevSize)
   }

   render(){
      return(
         <div>
            <IconButton onClick={()=>this.handleDelete()} aria-label='delete'>
               <DeleteIcon 
                  fontSize='small'
               />
            </IconButton>
            <TextField
               label='Size'
               value={this.state.size}
               onChange={...}
            />
            <TextField
               label='Amount left in this size'
               type='number'
               value={this.state.amount}
               onChange={...}
            />
        </div>
     )
  }
}

export default EditSize;

I'm not sure why when I map this.state.sizes in EditSizes component the item parameter is right, yet it doesn't pass the correct one to the _size prop of EditSize. What is missing?

Upvotes: 1

Views: 166

Answers (1)

Sumanth Madishetty
Sumanth Madishetty

Reputation: 3605

After going through your questions, Have changed some of the implementation of yours

I don't exactly understand why your are maintaining state in every component. You can store the state in one component and use it in the children where ever necessary

I have changed some of your code, And there are a lot of optimisations that can be done. Please go through the sandbox and feel free to ask questions.

Please refer to this sanbox

Upvotes: 1

Related Questions