简体   繁体   中英

onChange of <select> element in child component not re-rendering the parent state in React, Warning: unmounted component

I'm lifting state up from child to parent and trying to render select elements with data from HTTP request. The select tag values within the Inv child component will show previously selected values, but I'm getting this error:

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

It looks like the error is coming from the Inv child component that is being passed props with this.state.identInvsData . That is the only time I get the error. The child components in the else statements that don't get the identInvsData don't get that error message. The parent component does not re-render, or "mount" the children.
Any ideas as to what I'm doing wrong?

Edit: The child components are populating the data from HTTP endpoint, but onChange it doesn't change the values in setState , only when I select the button Add Inv . It adds another child but also allows the previous children to change.

Identity Parent Component

class Identity extends React.Component {  

      state = {
        invIds: [],
        inTypes: [],
        invSeqIds: [],
        numChildren: 0,
        addInvBtnPressed: false
      };

      handleAddInv = () => {
        this.setState({
          numChildren: this.state.numChildren + 1,
          addInvBtnPressed: true
        });
      };

      onChangeInv = (e, index, sId) => {
        const invIdsArry = this.state.invIds;
        const invSeqIdsArry = this.state.invSeqIds;
        const newId = [
          ...invIdsArry.slice(0, index),
          (invIdsArry[index] = e.target.value),
          ...invIdsArry.slice(index + 1)
        ];
        if (sId) {
          const newSeqId = [
            ...invSeqIdsArry.slice(0, index),
            (invSeqIdsArry[index] = sId),
            ...invSeqIdsArry.slice(index + 1)
          ];
          this.setState({ invIds: newId, invSeqIds: newSeqId });
        } else {
          this.setState({ invIds: newId });
        }
      };

      onChangeType = (e, index) => {
        const invTypesArry = this.state.invTypes;
        const newTypes = [
          ...invTypesArry.slice(0, index),
          (invTypesArry[index] = e.target.value),
          ...invTypesArry.slice(index + 1)
        ];
        this.setState({ invTypes: newTypes });
      };


      render() {
        const children = [];
        var i;
        if (this.state.identInvsData && !this.state.addInvBtnPressed) {
          for (i = 0; i < this.state.numChildren; i += 1) {
            children.push(
              <Inv
                key={i}
                invKey={i}
                onChangeInv={this.onChangeInv.bind(this)}
                onChangeType={this.onChangeType.bind(this)}
                invId={this.state.identInvsData[i].invId}
                invType={this.state.identInvsData[i].invTypeCd}
                seqId={this.state.identInvsData[i].seqId}
                invData={this.state.identInvsData[i]}
              />
            );
          }
        } else if (this.state.identInvsData && this.state.addInvBtnPressed) {
          for (i = 0; i < this.state.numChildren; i += 1) {
            children.push(
              <Inv
                key={i}
                invKey={i}
                onChangeInv={this.onChangeInv.bind(this)}
                onChangeType={this.onChangeType.bind(this)}
              />
            );
          }
        } else {
          for (i = 0; i < this.state.numChildren; i += 1) {
            children.push(
              <Inv
                key={i}
                invKey={i}
                onChangeInv={this.onChangeInv.bind(this)}
                onChangeType={this.onChangeType.bind(this)}
              />
            );
          }
        }
        return (
          <div>
            <button
              type="button"
              className="btn btn-info"
              onClick={this.handleAddInv}
            >
              Add Inv
            </button>
            <div>{children}</div>
          </div>
        )
    }
}
export default Identity;

Inv Child Component

class Inv extends React.Component {

    state = {
      kddLookupData: "",
      invData: ""
    };

    componentDidMount = () => {
      kddlookups_getAll().then(resp => {
        this.setState({
          kddLookupData: resp.data.item
        });
      });
      invs_getAll().then(resp => {
        this.setState({
          invData: resp.data.items
        });
      });
    };

    handleInvestigatorChange = e => {
      this.props.onChangeInv(e, this.props.invKey, this.props.seqId);
    };

    handleInvestigatorTypeChange = e => {
      this.props.onChangeType(e, this.props.invKey);
    };

    render() {
      return (
        <div>
          <select
            value={this.props.invId}
            name={this.props.invKey}
            onChange={this.handleInvChange.bind(this)}
          >
            <option className="blank">Select inv name:</option>
            {this.state.invData &&
             this.state.invData.map(inv => {
                return (
                   <option key={inv.userId} value={inv.userId}>
                      {inv.invName}
                   </option>
                );
              })}
          </select>

          <select
            value={this.props.invType}
            name={this.props.invKey}
            onChange={this.handleInvTypeChange.bind(this)}
          >
            <option className="blank">Select inv type:</option>
            {this.state.kddData &&
             this.state.kddData.kdd_inv_type.map(inv => {
               return (
                  <option key={inv.inv_type_cd} value={inv.inv_type_cd}>
                     {inv.inv_type_name}
                  </option>
               );
             })}
          </select>
        </div>
    )
  }
}
export default Inv;

It doesn't look like you are returning anything in your render function in the child component. Try:

render() {
return (
   <div>
      <select
        value={this.props.invId}
        name={this.props.invKey}
        onChange={this.handleInvChange.bind(this)}
      >
        <option className="blank">Select inv name:</option>
        {this.state.invData &&
         this.state.invData.map(inv => {
            return (
               <option key={inv.userId} value={inv.userId}>
                  {inv.invName}
               </option>
            );
          })}
      </select>

      <select
        value={this.props.invType}
        name={this.props.invKey}
        onChange={this.handleInvTypeChange.bind(this)}
      >
        <option className="blank">Select inv type:</option>
        {this.state.kddData &&
         this.state.kddData.kdd_inv_type.map(inv => {
           return (
              <option key={inv.inv_type_cd} value={inv.inv_type_cd}>
                 {inv.inv_type_name}
              </option>
           );
         })}
      </select>
      </div>
   );
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM