Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React is sending old state to its parent

When I'm sending state of the child component to its parent component, React is sending old state to the parent component.

I want to send the updated state on every click on ListItem which is properly working and calling the function handleItemClick.

But when I'm calling sendStateToParent. It is passing old state of it. Suppose I have clicked on ITEM1, it is sending empty array[]. Next I have clicked on ITEM2, it is sending array [ITEM1].

Here I'm actually creating a multiselect dropdown. which also can act as single select based on props it is getting.

import React from 'react';
import ListItemComponent from './ListItem.jsx';
import DropDownButtonComponent from './DropDownButton.jsx';
import DropDownStyle from '../../../../css/sass/drop-down.scss';

module.exports = React.createClass({
  handleClick: function () {
    this.setState({open: !this.state.open});
  },
  getInitialState: function () {
    return {
      open: false,
      //listItems: this.props.listItems,
      selectedItems:[],
      title: this.props.dropdownTitle
    }
  },
  handleItemClick: function (item) {
    var selectedItems = [];
    if(this.props.multiple == true){
      selectedItems = this.state.selectedItems;
      if(selectedItems.indexOf(item)==-1){
        selectedItems.push(item);
      }else{
        selectedItems.splice(selectedItems.indexOf(item),1)
      }
      this.setState({
        title: this.state.selectedItems.length+" selected",
        selectedItems: selectedItems
      });
    } else{
      selectedItems = [];
      selectedItems.push(item);
      this.setState({
        title: item,
        selectedItems: selectedItems,
        open: false
      });
    }
    this.sendStateToParent();
  },
  sendStateToParent: function(){
    this.props.ifListChanged(this);
  },
  handleTextChange: function (event) {
    var filteredItems = [];
    this.props.listItems.map(function(item){
      if(item.toLowerCase().search(event.target.value.toLowerCase()) != -1){
        filteredItems.push(item);
      }
    },this);
    this.setState({
      listItems: filteredItems
    });
  },
  clearSelected: function(){
    this.setState({
      title: this.props.dropdownTitle,
      selectedItems: [],
    });
  },
  render: function () {
    var index = 0;
    var list=[];
    if (this.state.listItems != undefined) {
      list = this.state.listItems.map(function (item) {
        return (
          <ListItemComponent
            key={index++}
            item={item}
            whenItemClicked={this.handleItemClick}
            className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
          />);
      }.bind(this));
    } else {
      list = this.props.listItems.map(function (item) {
        return (
          <ListItemComponent
            key={index++}
            item={item}
            whenItemClicked={this.handleItemClick}
            className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
          />);
      }.bind(this));
    }

    return <div className="btn-group bootstrap-select form-control">
      <DropDownButtonComponent
        whenClicked={this.handleClick}
        title={this.state.title}
      />
      <ul className={"dropdown-menu inner dropdown-menu " + (this.state.open ? "show" : "") }>
        {this.props.search? <li><input type="text" style={{margin:"auto", maxWidth:"96%"}} onChange={this.handleTextChange} placeholder="Search"/></li> :""}
        <li className="disabled"><a>Select from below list {this.props.multiple ? <i title="clear all" style={{fontSize:"15px"}} onClick={this.clearSelected} className="text-danger fa fa-ban pull-right"></i>: ""}</a></li>
        {list}
      </ul>
    </div>
  }
});

Thanks in advance

like image 906
Akhil P Avatar asked May 18 '16 10:05

Akhil P


People also ask

How to send current state to parent in react?

So in your code, you send a request to update state, and before the new state is processed, you call the method in the parent, to inform parent about state, which still has the old items. Fix 1:send current state to parent. To get the item to send the current state, you could use the callback that setState()provides. Explained also in react pages:

What is react context and how to use it?

— React Context provides a way to pass data through the component tree without having to pass props down manually at every level ☘️ In any application, data is essential. We all know this, in React, state and props are two very important properties and are commonly used, easily explained: they are all used to save the state of the data.

How to pass callback from parent to child in React component?

You pass the callback method to the child when it's created. class Parent extends React.Component { myCallback = (dataFromChild) => { //use dataFromChild }, render() { return ( <div> <ComponentA callbackFromParent={this.myCallback}/> </div> ); } }

How to create a react app using React Native?

Step 1: Create a React application using the following command: Step 2: After creating your project folder, i.e., folder name, move to it using the following command: Project Structure: It will look like the following. Step 3: Now create Parent and Children components in the src folder with the following code.


1 Answers

The reason parent gets the old value of selecteditems, is because setState() is an asynchronous operation. See explanation here:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

So in your code, you send a request to update state, and before the new state is processed, you call the method in the parent, to inform parent about state, which still has the old items.

Fix 1: send current state to parent.
To get the item to send the current state, you could use the callback that setState() provides. Explained also in react pages:

The second (optional) parameter is a callback function that will be executed once setState is completed and the component is re-rendered.

This ensures that the call to parent is made only after setState() is finished. Something like this:

this.setState(
  {
    title: item,
    selectedItems: selectedItems,
    open: false
  },
  this.sendStateToParent
);

Fix 2(optional but recommended): move selecteditems state to parent.
If your parent needs to know about selecteditems, I would advise not to put these in state of the list. The only thing your component does with the selecteditems is to send them to the parent, every time an item is clicked.
Instead, it is better to:

  • put selectedItems in state of the parent
  • from the parent, pass selectedItems as props to the component
  • move the handleItemClick logic to the parent
  • inside the parent, you update the list of selectedItems, and set state (of the parent)
  • triggering a re-render of the list, with the new selectedItems as props
like image 180
wintvelt Avatar answered Sep 17 '22 20:09

wintvelt