admin管理员组

文章数量:1317909

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

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

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

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

Share Improve this question edited Nov 19, 2017 at 23:57 Damjan Pavlica 34.1k10 gold badges75 silver badges78 bronze badges asked May 18, 2016 at 10:11 Akhil PAkhil P 1,6202 gold badges21 silver badges30 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

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 pleted and the ponent 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 remended): 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 ponent 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 ponent
  • 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

本文标签: javascriptReact is sending old state to its parentStack Overflow