表现部分不断重新渲染

时间:2018-03-08 20:43:20

标签: reactjs react-redux

编辑:我发现了问题。它是我发布的第三个组件代码。我试图调用goToBook并选择Book。因此,当用户在项目中点击书籍时,它将转到历史记录并将用户传递到路线。如果用户直接进入路线,我想直接调用goToBook,所以我检查道具是否将selectBook定义为undefined,如果是,则:

componentDidUpdate(){//wondering if I should do this inside compoentWillRecieveProps???
    const { match: { params } } = this.props;
    //console.log(this.props.selectBook)
    if(typeof this.props.selectBook !== 'undefined'){
        if(this.state.changedItem !== this.props.goToBook ){
            this.props.selectBook(params.bookID);
            //this.setState({changedItem: params.bookID}); Want to change state here, but cannot do this because I am in componentDidUpdate
        }
    }
}

问题是如果我取消注释控制台,它会持续运行,因为它会看到selectBook不断被调用。所以我需要以某种方式检查selectBook prop并确保它没有改变。任何有关这方面的帮助将不胜感激。

--- ORIGINAL MESSAGE ---所以我有以下代码:

class ArrayList extends React.Component {
    constructor(props){
        super(props);
        this.state = {

        };
        this.createListItem = this.createListItem.bind(this);
        this.handleDelete = this.handleDelete.bind(this);
        this.getCollection = this.getCollection.bind(this);
    }
    render () {
        return (
            <ul className="user-list">
                {this.props.items.map(this.createListItem)}
            </ul>
        );
    }
    createListItem(item) {
        if(item.id){
            return (
                <li key={item.id }>
                    {item.name}
                    {this.props.isEditing ? <Button className="deleteMe" onClick={this.handleDelete(this, item)}>Delete</Button> :''}
                </li>
            );
        }else if(item.AwardKey) {
            return (
                <li key={item.AwardKey }>
                    {item.Award}{item.AwardYear?'-':''}{item.AwardYear}
                    {this.props.isEditing ? <Button className="deleteMe" onClick={this.handleDelete(this, item)}>Delete</Button> :''}
                </li>
            );
        }else if(item.RelBookUseInKey) {
            return (
                <li key={item.RelBookUseInKey }>
                    {this.getCollection(item.CollectionForUseInKey)}{item.CollectionNumber?'-':''}{item.CollectionNumber}{item.Rating?'-':''}{item.Rating}{item.RelBookUseInKey}
                    {this.props.isEditing ? <Button className="deleteMe" onClick={this.handleDelete(this, item)}>Delete</Button> :''}
                </li>
            );
        }else if(item.RelBookDoNotUseInKey){
            return (
                <li key={item.RelBookDoNotUseInKey }>
                    {this.getCollection(item.CollectionForUseInKey)}{item.CollectionNumber && item.item.CollectionForUseInKey?'-':''}{item.CollectionNumber}{item.Rating?'-':''}{item.Rating}{item.RelBookDoNotUseInKey}
                    {this.props.isEditing ? <Button className="deleteMe" onClick={this.handleDelete(this, item)}>Delete</Button> :''}
                </li>
            );
        }
    }
    getCollection(collectionVal){
        if(!collectionVal) return '';
        let obj = this.props.collection.find((x)=>{
            return x.value === collectionVal
        });
        return obj.label;
    }

    handleDelete(e){
        if(this.props.handleDelete) {
            this.props.handleDelete(e);
        }
    }
}export default ArrayList;

这个表现部分不断呈现并且不会停止。我知道这是因为我在我的渲染前面放了一个console.log,它不断运行,减慢了我的处理器速度。有没有办法让一个演示组件只在道具改变时渲染?我认为这是反应/减少的重点。我想我可能会对这个组件设置错误。就像redux工作流程可能正在更新触发此组件重新渲染的内容。所以我想知道这个组件的生命周期是否依赖于其父数据来改变?

关于这个组件试图做什么:我希望将一个数组传递给它,并根据数组的内容创建一个不同类型的数组构建器。一些数组构建器具有不同类型的表单元素,以向数组添加不同的项。 这是父组件:

class ArrayValuesEdit extends React.Component {
    constructor(){
        super();
        this.state = {
            displayText: '',
            displayObj:{},
            displayArray:[],
            isEditing: false,
            newItem:0,
            checkProps:false,
            entryType: 'singleText'

        };
        this.onClickEdit = this.onClickEdit.bind(this);
        this.handleSingleSaveEdit = this.handleSingleSaveEdit.bind(this);
        this.handleAwardSaveEdit = this.handleAwardSaveEdit.bind(this);
        this.handleUseInSaveEdit = this.handleUseInSaveEdit.bind(this);
        this.handleDoNotSaveEdit = this.handleDoNotSaveEdit.bind(this);
        this.handleTextChanged = this.handleTextChanged.bind(this);
        this.handleMultipleTextChanged = this.handleMultipleTextChanged.bind(this);
        this.handleKeyPress = this.handleKeyPress.bind(this);
        this.renderList = this.renderList.bind(this);
        this.onSelectionHandle = this.onSelectionHandle.bind(this);
        this.bindSelectionHandle = this.bindSelectionHandle.bind(this);
        this.handleDelete = this.handleDelete.bind(this);
        this.drawEntryType = this.drawEntryType.bind(this);
    }

    componentDidMount(){
        if(typeof this.props.display !== 'undefined' && typeof this.props.displayArray !== 'undefined'){

            let result = this.props.options.find(x => x.value === this.props.val);
            const displayText = result.text;
            this.setState( {displayText} );
        }
    }

    componentWillReceiveProps(nextProps) {
        // You don't have to do this check first, but it can help prevent an unneeded render
        if (nextProps.displayArray !== this.state.displayArray && !this.state.checkProps) {
            this.setState({ displayArray: nextProps.displayArray, isEditingAll: nextProps.isEditingAll, readOnly: nextProps.readOnly, checkProps: true });
        }
        if (nextProps.entryType !== this.state.entryType)this.setState({entryType: nextProps.entryType});
    }
    onClickEdit(){
        this.setState({isEditing: !this.state.isEditing});
    }
    handleUseInSaveEdit(){
        let newObj = {RelBookUseInKey:this.state.newItem, CollectionForUseInKey: this.state.displayObj.CollectionForUseInKey, CollectionNumber: this.state.displayObj.CollectionNumber, Rating: this.state.displayObj.Rating};
        //this.props.handleSingleSaveEdit();
        console.log(newObj);
        let newState = update(this.state, {newItem: {$apply: function(x) {return x +1;}}, isEditing:{$set:false}, displayArray:{$push: [newObj]}});
        this.setState(newState, ()=>{console.log(this.state)});
    }
    handleDoNotSaveEdit(){
        let newObj = {RelBookDoNotUseInKey:this.state.newItem, CollectionForUseInKey: this.state.displayObj.CollectionForUseInKey, CollectionNumber: this.state.displayObj.CollectionNumber, Rating: this.state.displayObj.Rating};
        //this.props.handleSingleSaveEdit();

        let newState = update(this.state, {newItem: {$apply: function(x) {return x +1;}}, isEditing:{$set:false}, displayArray:{$push: [newObj]}});
        this.setState(newState);

    }
    handleAwardSaveEdit(){
        let newObj = {AwardKey:this.state.newItem, Award: this.state.displayObj.Award, AwardYear: this.state.displayObj.AwardYear};
        //this.props.handleSingleSaveEdit();

        let newState = update(this.state, {newItem: {$apply: function(x) {return x +1;}}, isEditing:{$set:false}, displayArray:{$push: [newObj]}});
        this.setState(newState);
    }
    handleSingleSaveEdit(){
        let newObj = {id:this.state.newItem, name: this.state.displayText};
        //this.props.handleSingleSaveEdit();
        let newState = update(this.state, {newItem: {$apply: function(x) {return x +1;}}, isEditing:{$set:false}, displayArray:{$push: [newObj]}});
        this.setState(newState);

    }
    handleTextChanged(e){
        //this.props.handleTextChanged();
        this.setState({displayText: e.target.value});
    }
    handleMultipleTextChanged(e){
        //this.props.handleTextChanged();
        this.setState({displayObj: {...this.state.displayObj, [e.target.name]:e.target.value}});
    }
    handleDropSelectChange(value, name){
        this.setState({displayObj: {...this.state.displayObj, [name]:value}});
    }
    handleKeyPress() {
        //this.props.handleKeyPress();
        this.setState({isEditing: !this.state.isEditing});
    };

    onKeyPress(){
        if(event.charCode === 13 && this.props.onKeyPress){
            this.onKeyPress(event);
        }
    }
    handleDelete(e){
    }
    onSelectionHandle( buttonText ) {
        this.setState({displayText: buttonText});
    }
    bindSelectionHandle( buttonText ) {
        return this.onSelectionHandle.bind( this, buttonText )
    }

    drawEntryType(){
        if(this.state.entryType === 'singleText'){
            return <InputContainer className="inputElem" onSaveEdit={this.handleSingleSaveEdit} onKeyPress={this.handleKeyPress} onTextChanged={this.handleTextChanged}/>
        }else if (this.state.entryType === 'awards'){
            return (
                <div>
                    Award:<input name="Award" className="inputValue" value={this.state.displayObj.Award || ''} type="text" onKeyPress={this.onKeyPress} onChange={this.handleMultipleTextChanged}/>
                        Year:<input name="AwardYear" className="inputValue" value={this.state.displayObj.AwardYear || ''} type="text" onKeyPress={this.onKeyPress} onChange={this.handleMultipleTextChanged}/>
                <button className="saveMe" onClick={this.handleAwardSaveEdit}>Save</button>
                </div>)
        }else if (this.state.entryType === 'usein'){
            return (<div>
                Collection:<Select
                closeOnSelect={true}
                disabled={false}
                multi={false}
                onChange={(val) => this.handleDropSelectChange(val, "CollectionForUseInKey")}
                options={this.props.collectionOptions}
                placeholder='Select Collection'
                removeSelected={false}
                rtl={false}
                simpleValue
                value={this.state.displayObj.CollectionForUseInKey}
            />
                Collection Number:<input name="CollectionNumber" className="inputValue" value={this.state.displayObj.CollectionNumber || ''}
                                         type="text" onKeyPress={this.onKeyPress} onChange={this.handleMultipleTextChanged}/>
                Rating:<Select
                closeOnSelect={true}
                disabled={false}
                multi={false}
                onChange={(val) => this.handleDropSelectChange(val, "Rating")}
                options={this.props.ratingOptions}
                placeholder='Select Rating'
                removeSelected={false}
                rtl={false}
                simpleValue
                value={this.state.displayObj.Rating}
                />
                <button className="saveMe" onClick={this.handleUseInSaveEdit}>Save</button>
            </div>)
        }else if (this.state.entryType === 'donot'){
            return (
                <div>
                    Collection:<Select
                    closeOnSelect={true}
                    disabled={false}
                    multi={false}
                    onChange={(val) => this.handleDropSelectChange(val, "Collection")}
                    options={this.props.collectionOptions}
                    placeholder='Select Collection'
                    removeSelected={false}
                    rtl={false}
                    simpleValue
                    value={this.state.displayObj.Collection}
                />
                    Reason:<input name="Reason" className="inputValue" value={this.state.displayObj.Reason || ''}
                                  type="text" onKeyPress={this.onKeyPress} onChange={this.handleMultipleTextChanged}/>

                    <button className="saveMe" onClick={this.handleDoNotSaveEdit}>Save</button>
            </div>)
        }else if (this.state.entryType === 'license'){
            return (<div>

            </div>)
        }
    }
    renderList(){

        if (typeof this.state.displayArray === "undefined"){
            return;
        } else if(this.state.displayArray){
            return this.state.displayArray.map((option)=>{
                return(
                    <Button eventKey={option.value} onClick={ this.bindSelectionHandle( option.text )}>{option.text}</Button>
                );
            });
        }

    }
    render(){
        return(
            <div className="display-choices">
                {this.state.isEditing ? this.drawEntryType() :''}
                <span className="clickMe" onClick={this.onClickEdit}><ArrayList collection={this.props.collectionOptions || []} items={this.state.displayArray} isEditing={this.state.isEditing} /> </span>
            </div>
        )
    }
}export default ArrayValuesEdit;

我还有以下连接的redux组件,它包含ArrayValuesEdit组件。为了保持代码简短,我将删除我为它编写的jsx,这不适用于重新渲染的原因。

export class BookDetail extends Component{
    componentDidMount(){

        Moment.locale('en');
        momentLocalizer();
    }
    componentWillReceiveProps(nextProps) {
        if(typeof this.props.getAbeReadingLevelOptions !== 'undefined')this.props.getAbeReadingLevelOptions();
        if(typeof this.props.getAgeAppropriateOptions !== 'undefined')this.props.getAgeAppropriateOptions();
        if(typeof this.props.getAgeOptions !== 'undefined')this.props.getAgeOptions();
        if(typeof this.props.getBindingTypeOptions !== 'undefined')this.props.getBindingTypeOptions();
        if(typeof this.props.getCitationOptions !== 'undefined')this.props.getCitationOptions();
        if(typeof this.props.getCollectionOptions !== 'undefined')this.props.getCollectionOptions();
        if(typeof this.props.getCultureOptions !== 'undefined')this.props.getCultureOptions();
        if(typeof this.props.getDistrictApprovalOptions !== 'undefined')this.props.getDistrictApprovalOptions();
        if(typeof this.props.getEthnicityOptions !== 'undefined')this.props.getEthnicityOptions();
        if(typeof this.props.getFormatOptions !== 'undefined')this.props.getFormatOptions();
        if(typeof this.props.getGenderOptions !== 'undefined')this.props.getGenderOptions();
        if(typeof this.props.getGuidedReadingLevelOptions !== 'undefined')this.props.getGuidedReadingLevelOptions();
        if(typeof this.props.getIsbnSeparationStatusOptions !== 'undefined')this.props.getIsbnSeparationStatusOptions();
        if(typeof this.props.getLexileCodeOptions !== 'undefined')this.props.getLexileCodeOptions();
        if(typeof this.props.getNarratorOptions !== 'undefined')this.props.getNarratorOptions();
        if(typeof this.props.getPrimaryGenreOptions !== 'undefined')this.props.getPrimaryGenreOptions();
        if(typeof this.props.getRatingOptions !== 'undefined')this.props.getRatingOptions();
        if(typeof this.props.getSecondaryGenreOptions !== 'undefined')this.props.getSecondaryGenreOptions();
        if(typeof this.props.getSensitiveContentOptions !== 'undefined')this.props.getSensitiveContentOptions();
        if(typeof this.props.getCountryOptions !== 'undefined')this.props.getCountryOptions();
    }

    componentDidUpdate(){
        const { match: { params } } = this.props;
        if(typeof this.props.selectBook !== 'undefined'){
            if(this.state.changedItem !== this.props.goToBook ){
                this.props.selectBook(params.bookID);
                this.state.changedItem = params.bookID;
            }

        }
    }
    constructor(){
        super();
        this.state = {
            isEditingAll: false,
            displayMeta:false,
            displayISBN:false,
            displayPrice:false,
            displayOps:false,
            displayLevel:false,
            displayTrain:false,
            displayAppro:false,
            displayLang:false,
            displayNYC:false,
            displayNotes:false,
            displayTopics:false,
            changedItem:0
        };

        this.onClickMeta = this.onClickMeta.bind(this);
        this.onClickISBN = this.onClickISBN.bind(this);
        this.onClickPrice = this.onClickPrice.bind(this);
        this.onClickOps = this.onClickOps.bind(this);
        this.onClickLevel = this.onClickLevel.bind(this);
        this.onClickTrain = this.onClickTrain.bind(this);
        this.onClickAppro = this.onClickAppro.bind(this);
        this.onClickLang = this.onClickLang.bind(this);
        this.onClickNYC = this.onClickNYC.bind(this);
        this.onClickNotes = this.onClickNotes.bind(this);
        this.onClickTopics = this.onClickTopics.bind(this);
        this.onImgError = this.onImgError.bind(this);
        this.removeSpaces = this.removeSpaces.bind(this);
        this.onCheckChanged = this.onCheckChanged.bind(this);
    }

    onClickMeta(){this.setState({displayMeta: !this.state.displayMeta});}
    onClickISBN(){this.setState({displayISBN: !this.state.displayISBN});}
    onClickPrice(){this.setState({displayPrice: !this.state.displayPrice});}
    onClickOps(){this.setState({displayOps: !this.state.displayOps});}
    onClickLevel(){this.setState({displayLevel: !this.state.displayLevel});}
    onClickTrain(){this.setState({displayTrain: !this.state.displayTrain});}
    onClickAppro(){this.setState({displayAppro: !this.state.displayAppro});}
    onClickLang(){this.setState({displayLang: !this.state.displayLang});}
    onClickNYC(){this.setState({displayNYC: !this.state.displayNYC});}
    onClickNotes(){this.setState({displayNotes: !this.state.displayNotes});}
    onClickTopics(){this.setState({displays: !this.state.displayNotes});}

    onImgError(e) {
        e.target.src = '/img/NoImageFound240px.jpg';
    }
    removeSpaces(val){
        return val.replace(/^\s+|\s+$/g,'');
    }

    onCheckChanged(e){
        let newVal;
        {e.target.checked ? newVal =  1 : newVal = 0};
        newVal === 1 ? this.setState({isEditingAll: true}) : this.setState({isEditingAll: false});
        this.setState({isEditingAll: newVal});

    }

    render(){
        const YES_NO = [{"value":-1, "label":"N/A"},{"value":0, "label":"No"},{"value":1, "label":"Yes"}];
        if(!this.props.book){
            return <div>Select a book to get started.</div>;
        }
        return(
            ......jsx is added here.....
        )
    }
}

function mapDispatchToProps(dispatch){
    return{
        getAbeReadingLevelOptions: bindActionCreators(actions.getAbeReadingLevelOptions, dispatch),
        getAgeAppropriateOptions: bindActionCreators(actions.getAgeAppropriateOptions, dispatch),
        getAgeOptions: bindActionCreators(actions.getAgeOptions, dispatch),
        getBindingTypeOptions: bindActionCreators(actions.getBindingTypeOptions, dispatch),
        getCitationOptions: bindActionCreators(actions.getCitationOptions, dispatch),
        getCollectionOptions: bindActionCreators(actions.getCollectionOptions, dispatch),
        getCultureOptions: bindActionCreators(actions.getCultureOptions, dispatch),
        getDistrictApprovalOptions: bindActionCreators(actions.getDistrictApprovalOptions, dispatch),
        getEthnicityOptions: bindActionCreators(actions.getEthnicityOptions, dispatch),
        getFormatOptions: bindActionCreators(actions.getFormatOptions, dispatch),
        getGenderOptions: bindActionCreators(actions.getGenderOptions, dispatch),
        getGuidedReadingLevelOptions: bindActionCreators(actions.getGuidedReadingLevelOptions, dispatch),
        getIsbnSeparationStatusOptions: bindActionCreators(actions.getIsbnSeparationStatusOptions, dispatch),
        getLexileCodeOptions: bindActionCreators(actions.getLexileCodeOptions, dispatch),
        getNarratorOptions: bindActionCreators(actions.getNarratorOptions, dispatch),
        getPrimaryGenreOptions: bindActionCreators(actions.getPrimaryGenreOptions, dispatch),
        getRatingOptions: bindActionCreators(actions.getRatingOptions, dispatch),
        getSecondaryGenreOptions: bindActionCreators(actions.getSecondaryGenreOptions, dispatch),
        getSensitiveContentOptions: bindActionCreators(actions.getSensitiveContentOptions, dispatch),
        selectBook: bindActionCreators(bookActions.selectBook, dispatch),
        getCountryOptions: bindActionCreators(getCountryOptions, dispatch)
    }
}

function mapStateToProps(state){
    return{
        options: state.options,
        book: state.activeBook,
        goToBook: state.goToBook
    };
}
export default connect(mapStateToProps, mapDispatchToProps)(BookDetail);

2 个答案:

答案 0 :(得分:1)

很可能是因为你在渲染中绑定它。每次为外部函数调用render时,这不仅会在运行时创建多个函数对象实例。如果Button是一个Pure组件,它应该是最有可能的,每次重新渲染Button时,它将为onClick创建一个新的函数对象引用,因为Buttons prop正在改变,因为它的引用是它将重新渲染

  onClick={this.handleDelete.bind(this, item)} //bad practice

在下面做,这将确保所有onClick引用一个在构造函数中绑定的函数对象引用,从而使您的组件更高效。

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

//bind it once use every where 
onClick={this.handleDelete}

并删除onClicks中的绑定。

答案 1 :(得分:0)

我使用componentDidMount和componentWillReceiveProps的混合来修复它,以确定是否命中了新路由并且是否设置了redux selectBook。似乎现在正在工作,没有不断刷新。