【问题标题】:prevent duplicate while merging redux array of objects with setState array将redux对象数组与setState数组合并时防止重复
【发布时间】:2019-11-19 16:45:30
【问题描述】:

首先一个普遍的问题是如何连接 setState 数组和 redux 数组?在我的实现中,有一个基于 setState 对象数组的配置列表。现在,我有一个基于 redux 对象数组的位置列表。现在我正在使用 concat 将位置列表中的一项添加到配置列表并保存数据。它已成功保存,但在合并列表中显示 3 个项目(即 redux 数组项目 2 次)。

但是,我检查了控制台,虽然它显示了正确的结果(2 项),但为什么它显示 3 项(redux 项 2 次)。

组件代码:(getLocationData 方法,我做了连接部分并使用回调引用在其他组件中调用该方法并且工作正常)

export class NewLocationPanel extends React.Component{
    constructor(props){
        super(props);
        this.state={
               open:false,
               configuredList:[],
               retrievedList:[]
        };
       this.configLocation = this.configLocation.bind(this);
        this.togglePanel = this.togglePanel.bind(this);
        this.handleClick = this.handleClick.bind(this);
        this.allLocations = this.allLocations.bind(this);
        this.clearall = this.clearall.bind(this);
        this.getLocationData = this.getLocationData.bind(this);
        this.handleRemove = this.handleRemove.bind(this);
        this.removeConfigLocation = this.removeConfigLocation.bind(this);
        this.mergedLocation = this.mergedLocation.bind(this);
    }

    togglePanel (e){
        this.setState({open : !this.state.open});
    }
    handleRemove(mruCode){
        this.props.removeLocation(mruCode)
     }
    handleClick (mruCode){
      this.props.addLocation(mruCode)
     }
     allLocations (){
       this.props.addAllLocation()
    }
    clearall (){
        this.props.removeAllLocation()
    }

    componentDidMount() {
        this.props.loadData();
        if(this.props.locationData !=null && this.props.locationData!= undefined){
            this.configLocation(this.props.locationData);
        }
      }

    componentDidUpdate(prevProps,prevState){
        if ((prevProps.jobId != this.props.jobId || prevProps.locationData != this.props.locationData) && this.props.locationData != null && this.props.locationData != undefined) {
            this.configLocation(this.props.locationData);
            this.mergedLocation();
        }
    }

    configLocation(locationData){
        let configuredList =[];
        if(locationData.locations.locationDetails != null && locationData.locations.locationDetails !=undefined ){
            locationData.locations.locationDetails.map(item=>{
                 let listitem ={...item};
                 configuredList.push(listitem);
            });
        }
        this.setState({configuredList},()=>{
            console.log(this.state.configuredList);
        }); 
    }

    removeConfigLocation(index){
        this.setState({
            configuredList:this.props.locationData.locations.locationDetails.filter((_,i)=>i!==index)
        },()=>{
            console.log(this.state.configuredList);
        });

    }


    mergedLocation(){
         if(this.props.conLocations != null && this.state.configuredList !=null){
             const{configuredList} = this.state;
             let retrievedList = configuredList;
             this.props.conLocations.forEach(loct => {
                 const locationAdded = retrievedList.find(_loct=>loct.mruCode=== loct.mruCode)
             });
          this.setState({
              retrievedList},()=>{
            console.log(this.state.retrievedList);
          });
         }  
    }

    getLocationData(){
        let saveableLocationlist = [];
        if(this.state.retrievedList != null){
            saveableLocationlist = retrievedList;
        }
        const locationData = {
            locationDetails : saveableLocationlist
        }
      return locationData;
    }



    render(){
        //const{configuredList} = this.state;
        const _labels = store.getLabels();
        let collapsedToggle = this.props.open ? 'collapsed' : ''
        return(
            <div className="panel panel-default">
            <div className="panel-heading" onClick={(e)=>this.togglePanel(e)}>
              <div className="row">
              <div className="col-xs-12 col-sm-8 col-md-6 col-lg-6 panelHeadingLabel">
                     <span>{this.props.title}</span>
                     </div>
                        <div className="pull-right">
                        <span className="defaultHeaderTextColor">{this.state.configuredList.map((loc,index)=><span key={index}>{loc.mruCode} - {_labels[loc.division]} - {loc.country}</span>)}
                           <span onClick={(e)=>this.togglePanel(e)} className={this.state.open ? "collapse-chevronn" : "collapse-chevron"} aria-hidden="true"></span>
                   </span>
                    </div>
                </div>
           </div>
              {this.state.open?(
                        <div className="panel-body">
                             <div className="row grid-divider">
                             <div className="col-sm-6">
                             <div className="col-padding"><div className="pos-div"><h3>Locations List</h3><button style={{ display: this.props.location.length === this.props.conLocations.length ? "none" : "block" }} className="allLargeBtn" onClick={()=>{this.allLocations()}}>Add all locations</button></div><hr/>
                             {this.props.location.map((item,index)=>(
                             <div key={index}><div><b>{item.mruCode} - {_labels[item.division]} - {item.country}</b>{!this.props.conLocations.find(item2 => item.mruCode === item2.mruCode)&&(<div className="pull-right jd"><button style={{ display: this.state.configuredList.find(item3=> item.mruCode===item3.mruCode) ? "none" : "block" }} className="call-to-action" onClick={()=>{this.handleClick(item.mruCode)}}>Add Location</button></div>)}<hr/></div></div>))}
                            </div>
                             </div> 
                                  <div className="col-sm-6">
                                  <div className="col-padding">
                                  <div className="pos-div"><h3>Configured Location</h3><button className="allLargeBtn" onClick={()=>this.clearall()}>Remove all location</button></div><hr/>
              <div><table className="table"><tbody>{this.state.retrievedList.map((locc,index)=><tr key={index}><td><b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b></td><td className="text-right"><img alt="DeleteIcon" onClick={()=>{this.removeConfigLocation(index)}} className="deleteIconStyle" src="img/delete_large_active.png" /></td></tr>)}
                    </tbody></table></div>

                                   </div>
                                  </div>
                                  </div> 
                    </div>):null}
            </div>

        );
    }
}

const mapStateToProps = state =>{
    return{
        location:state.locationRed.location,
        conLocations:state.locationRed.conLocations
    };
};

const mapDispatchToProps = (dispatch) => {
    return{
        loadData:()=>{dispatch(loadData())},
        addLocation:(mruCode)=>{dispatch(addLocation(mruCode))},
        addAllLocation:() =>{dispatch(addAllLocation())},
        removeLocation: (mruCode)=>{dispatch(removeLocation(mruCode))},
        removeAllLocation: () =>{dispatch(removeAllLocation())}
    }
}


export default connect(mapStateToProps,mapDispatchToProps,null,{withRef:true})(NewLocationPanel);

工作组件(我调用 getLocationData 来保存工作的更新值...它正在正确保存详细信息)

import React from 'react';
import ReactDOM from 'react-dom';
import LocationPanel from '../panels/NewLocationPanel';

class JobsPanelComponent extends React.Component {

    constructor(props) {
        super(props);
        this.state = { 
            jobDetailJson: this.props.jobDetailJson

        };
this.setLocationPanelRef = cRef =>{this.locationPanel = cRef;};

}
componentWillUnmount() {
        this.clearStates();
        this.clearRefs();
        this.clearBindings();
    }
          clearStates() {

        this.state.jobDetailJson = null;
        }
        clearRefs(){
               this.locationPanel = null;
                   }
        clearBindings(){
               this.setLocationPanelRef = null;
                       }
        componentWillMount() {
        this.state.jobDetailJson = this.props.jobDetailJson;
    }

    componentWillReceiveProps(nextProps) {
        this.state.jobDetailJson = nextProps.jobDetailJson;
    }
     saveJobData(jobData){
      var locationData = null;
       if(some conditions){
        locationData = this.locationPanel.getWrappedInstance().getLocationData();
      }
    //more other lines not related to my mine
     }
      render(){
         var locationDataJson= null;
             if(this.state.jobDetailJson != null){
                     locationDataJson =this.state.jobDetailJson;
                   }
         return(<div className="panel-group" id="jobsPanelGroup">
               <LocationPanel ref={this.setLocationPanelRef} locationData ={locationDataJson} jobDetailJson={this.state.jobDetailJson} versionId={versionId} jobName={jobName} jobId={jobId} isForViewOnly={this.props.isForViewOnly} parentJobId={this.props.parentJobId} title="Location"/>
     //More coded lines for other things not related to my part
              );
         }


}

我正在添加显示 3 个结果的当前输出,但它应该显示 2 个结果。如何防止这种情况。请帮助我。

【问题讨论】:

  • 将console.log 放入getLocationData 以获取配置列表和conLocations。如果有帮助,请告诉我,以便我写一个答案。
  • 是的,configuredList 是第一个数据值,如图片。对于添加单个项目后的 conLocations,它显示第二个数据值。是的,我以不同的方式渲染了两个数组。

标签: javascript arrays reactjs redux


【解决方案1】:

更新 #1:

mergeLocationData(){
  let mergedList = [];    
  // you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`. 
  if(this.props.conLocations !== undefined && this.state.configuredList !== null){
    const { configuredList } = this.state;
    const { conLocations } = this.props;
    mergedList = configuredList;

    this.props.conLocations.forEach(location => {
      const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
      if(!locationAdded){
        mergedList.push(location)
      }
    });
  }
    // instead of setting state, return the mergedList
    return mergedList; //[ always an array of elements ]
}

然后,在 render 方法中:我们只是调用函数,而不是从状态中获取列表。

<thead>
  {
    this.mergeLocationData().map((locc,index)=> (
      <tr key={index}>
        <th>
          <b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
        </th>
        <th className="text-right">
          <img
            alt="DeleteIcon"
            onClick={()=>{this.removeConfigLocation(index)}}
            className="deleteIconStyle"
            src="img/delete_large_active.png" />
        </th>
      </tr>
    )
  }
</thead>

代码没有任何问题,我想它可以正常工作,但是,这是不正确的逻辑,您在渲染中渲染了两个数组,您没有使用函数 getLocationData anyware组件,尽管concat 不能解决问题。

您可以执行以下步骤来修复逻辑。 - 修复getLocationData的逻辑:

mergeLocationData(){      
  // you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`. 
  if(this.props.conLocations !== undefined && this.state.configuredList !== null){
    const { configuredList } = this.state;
    const { conLocations } = this.props;
    let mergedList = configuredList;

    this.props.conLocations.forEach(location => {
      const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
    });

    this.setState({
        mergedList
    });
  }
}
  • 使用该函数作为对您的获取请求的回调,可能在正确加载数据后在componentDidMount 中。

  • 渲染mergedList 的新Array,它现在应该处于您的状态,不要忘记将mergedList: [] 添加到您的状态。 基本上,您需要用它替换在渲染中渲染位置元素的两个地图。

    <thead>
      {
        this.state.mergedList.map((locc,index)=> (
          <tr key={index}>
            <th>
              <b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
            </th>
            <th className="text-right">
              <img
                alt="DeleteIcon"
                onClick={()=>{this.removeConfigLocation(index)}}
                className="deleteIconStyle"
                src="img/delete_large_active.png" />
            </th>
          </tr>
        )
      }
    </thead>

【讨论】:

  • 非常感谢您的回复。是的,这就是我搜索的方式。但是,在其中添加位置时存在一个问题。位置添加??怎么用?我只是对这部分感到困惑
  • 如果你的意思是在合并它时,这是我们检查对象是否存在并根据它推送或不推送的位mergedList.find(_location =&gt; _location.mruCode === location.mruCode);
  • 我已经编辑了我在哪里使用 getLocationData 的问题。我将数据保存到 locationData 并将该方法调用到该作业组件中。它正在正确保存。现在如何在您的 mergeLocationData () 部分执行此操作。您能否在您的代码中也添加这部分?所以我可以编辑和测试
  • 非常感谢。这是我经过这么多试验后理解的方式。只需告诉我如何根据您的回答配置 mergeLocationData() 中的保存 locationData 部分
  • 如果将数据保存在store中,然后再次调用merge函数,应该没问题。
猜你喜欢
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 2012-05-28
  • 2021-11-12
  • 1970-01-01
  • 2018-06-26
相关资源
最近更新 更多