2017-05-03 3 views
0

私は簡潔な方法でこの質問をするのに苦労しています。私は私のアプリでいくつかの主要なパフォーマンスの問題を抱えています。私は反応のためのPerfアドオンツールをインストールしましたが、どこに問題があるかを確認することができますが、それを修正する最良の方法がわかりません。React-Redux - 無関係のコンポーネントに不必要な再レンダリングを引き起こすことなく状態を更新する方法

私はおそらくReSelectと関係があると思いますが、どこから始めるべきかについてのガイダンスが必要です。

私は他の多くのコンポーネントをレンダリングするコンポーネントを持っています。これは、size-me(ブラウズウィンドウのサイズを計算する)、および反応グリッドレイアウト(各コンポーネントをレイアウトし、それらの配置を変更できるようにする)を使用します。これはリソースを大量に消費するので、これは不必要に起こることはありません。

ユーザーはボタンをクリックして、モーダルウィンドウを開くことができます(グリッドにレンダリングされているコンポーネントを追加または編集するため)。

問題:モーダルウィンドウが開くと、元のコンポーネントが再描画され、size-meとリアクショングリッドレイアウトが再描画され、モーダルが「ぎくしゃくして」開くようになります。

これは、全体の状態の木である:これは私がモーダルを開くときに変化する状態の一部でしかありません This is the entire state tree

This is the only part of the state that changes when I open the modal

サイズ-私と反応し、グリッドレイアウトをもの状態ツリーのformEngine.form部分から状態をレンダリングしますが、ツリーのEEngine.addComponent部分の状態が更新されたときにレンダリングされます

ここにパフォーマンスログがあります: Here are the performance logs

あなたが見ることができるように、そこにいくつかが無駄になる起こっレンダリングし、これが唯一のユーザーがフォームに追加することを決定、ネストされたレイアウト要素の数に基づいて段階的に拡張されます...

そうしようとすると、あまりにも複雑になることから、この質問を防止するため、私は最初を尋ねてみましょう:

  1. 私はモーダルを開いたときにどのように再レンダリングから基本的なページを防止していますか?
  2. fromEngine.addComponentが変更されたときに、formEngine.formを監視しているコンポーネントが再描画をトリガーしたのはなぜですか?

ありがとうございます。


EDIT 1:

私はこれが関連しているかどうかわからないんだけど、コメントに答えるために、私はこのコードを追加しました。 AddFormComponentは、ジャークが開くモーダルです。

Form.js:

const Form = (props) => (
    <div className="form-engine"> 
    <div className="card-block" style={{position: "relative"}}> 
     { 
     props.editMode && 
     <div className="nula-form-controls"> 
      <AddFormComponent parentId={"root"} /> 
     </div> 
     }    
     { 
     props.form.components.root.childComponentIds.length > 0 ? 
      <LayoutComponent componentKey={"root"} /> 
     : 
      <EmptyGridLayout /> 
     } 
    </div> 
    </div> 
) 

LayoutComponent。JS:

import React from 'react' 
 
import _ from 'lodash' 
 
import SizeMe from 'react-sizeme' 
 
import { Responsive as ResponsiveReactGridLayout } from 'react-grid-layout' 
 
import 'react-grid-layout/css/styles.css' 
 
import 'react-resizable/css/styles.css' 
 

 
import FormComponent from '../containers/FormComponent' 
 
import NestedLayoutComponent from '../containers/LayoutComponent' 
 

 
import AddFormComponent from '../containers/AddFormComponent' 
 
import LayoutComponentEditor from '../containers/LayoutComponentEditor' 
 

 
//Setup SizeMe Configuration 
 
let sizeMeConfig = { 
 
    monitorWidth: true 
 
} 
 
let sizeMeHOC = SizeMe(sizeMeConfig) 
 

 
//Wrap ResponsiveReactGridLayout in sizeMeHOC so that it is aware of it's width 
 
var GridLayout = ResponsiveReactGridLayout 
 
GridLayout = sizeMeHOC(GridLayout) 
 

 
const LayoutComponent = (props) => (
 
    <div> 
 
    <GridLayout 
 
     cols={props.cols} 
 
     className={props.className} 
 
     breakpoints={props.breakpoints} 
 
     rowHeight={props.rowHeight} 
 
     draggableCancel={props.draggableCancel} 
 
     layouts={props.layouts} 
 
     isDraggable={props.isDraggable} 
 
     isResizable={props.isResizable} 
 
     onLayoutChange={(currentLayout, allLayouts) => props.handleLayoutChange(props.componentKey, currentLayout, allLayouts)} 
 
     width={props.size.width} 
 
    > 
 
     { 
 
     //Map out any child layouts 
 
     props.childComponents.map((component) => { 
 
      if (component.type === "card") { 
 
      return (
 
       <div className={"card card-outline-" + component.color} key={component.key}> 
 
       <div className={"card-header card-" + component.color}> 
 
        {component.header} 
 
       </div> 
 
       <div className="card-block" style={{overflowY: "auto", position: "relative"}}> 
 
        { 
 
        //Hide if editMode={false} 
 
        props.editMode && 
 
         <div className="nula-card-controls"> 
 
         <LayoutComponentEditor path={component.key} /> 
 
         <a href="#" className="text-danger" title="Remove"><span className="fa fa-trash" /></a> 
 
         <AddFormComponent parentId={component.key} /> 
 
         </div> 
 
        }     
 
        <NestedLayoutComponent componentKey={component.key} /> 
 
       </div>    
 
       </div> 
 
      ) 
 
      } 
 
      else if (component.type === "fieldGroup") { 
 
      return (
 
       <div className="card" key={component.key}> 
 
       <div className="card-block pl-0 pr-0 pt-2 pb-0" style={{overflowY: "auto"}}> 
 
        { 
 
        //Hide if editMode={false} 
 
        props.editMode && 
 
         <div className="nula-fieldgroup-controls"> 
 
         <a className="text-warning" title="Edit"><span className="fa fa-pencil" /></a> 
 
         <a className="text-danger" title="Remove"><span className="fa fa-trash" /></a> 
 
         <AddFormComponent parentId={component.key} /> 
 
         </div> 
 
        }     
 
        <NestedLayoutComponent componentKey={component.key} /> 
 
       </div>    
 
       </div>     
 
      ) 
 
      } 
 
      else if (component.type === "paragraph") { 
 
      return (
 
       <div className="alert alert-success text-font-bold" key={component.key}> 
 
       { 
 
        <FormComponent component={component} editMode={props.editMode} /> 
 
       } 
 
       </div> 
 
      ) 
 
      } 
 
      else { 
 
      return (
 
       <div key={component.key}> 
 
       { 
 
        <FormComponent component={component} editMode={props.editMode} /> 
 
       } 
 
       </div>     
 
      ) 
 
      }   
 
     }) 
 
     } 
 
    </GridLayout> 
 
    </div> 
 
) 
 

 
export default SizeMe()(LayoutComponent)


EDIT 2:

AddFormComponent.js - コンポーネント

import React from 'react' 
 
import AddFormComponentDetails from './AddFormComponentDetails' 
 

 
import Perf from 'react-addons-perf'; // ES6 
 

 
class AddFormComponent extends React.Component { 
 

 
    constructor(props) { 
 
    super(props); 
 
    this.localOpenModal = this.localOpenModal.bind(this); 
 
    } 
 

 
    localOpenModal() { 
 
    console.log("----STARTING PERFORMANCE MONITOR-----") 
 
    Perf.start() 
 
    this.props.handleOpenModal(); 
 
    } 
 

 
    componentDidUpdate() { 
 
    console.log("-----PERFORMANCE MONITOR STOPPING------") 
 
    Perf.stop() 
 
    console.log("-----PRINT INCLUSIVE------") 
 
    Perf.printInclusive() 
 
    console.log("-----PRINT WASTEED------") 
 
    Perf.printWasted() 
 
    } 
 

 
    render() { 
 
    return (
 
    <span> 
 
    <a onTouchTap={this.localOpenModal} className="text-success" title="Add Component"> 
 
     <span className="fa fa-plus" /> 
 
    </a> 
 

 
    <Modal isOpen={this.props.modalOpen} size={"lgr"} toggle={this.props.handleCloseModal}> 
 
     <ModalHeader toggle={this.props.handleCloseModal}>Add Component</ModalHeader> 
 
     <ModalBody> 
 
     ...Removed For Breviety 
 
     </ModalBody> 
 
     <ModalFooter> 
 
     ...Removed For Breviety  
 
     </ModalFooter> 
 
    </Modal> 
 
    </span> 
 
) 
 
    } 
 
} 
 

 
export default AddFormComponent

AddFormComponent.js - コンテナ

import { connect } from 'react-redux' 
 
import { 
 
    handleOpenModal, 
 
    handleCloseModal, 
 
    handleGoBack, 
 
    handleComponentPropertyChange, 
 
    handleComponentNameChange, 
 
    handleComponentTypeChange, 
 
    handleSubmit 
 
} from '../actions/addFormComponentActions' 
 
import AddFormComponent from '../components/AddFormComponent' 
 

 
const mapStateToProps = (state) => ({ 
 
    steps: [ 
 
    { icon: 'superpowers', title: 'Select Component', description: 'Select the Component you wish to add', active: state.addComponent.currentStep == 1 }, 
 
    { icon: 'info circle', title: 'Enter Details', description: 'Enter details to customize component', active: state.addComponent.currentStep == 2 }, 
 
    { icon: 'check', title: 'Add Component', description: 'Add component to form' } 
 
    ], 
 
    currentStep: state.addComponent.currentStep, 
 
    modalOpen: state.addComponent.modalOpen, 
 
    component: state.addComponent.component, 
 
    errors: state.addComponent.errors, 
 
    componentType: state.addComponent.componentType 
 
}) 
 

 
export default connect(
 
    mapStateToProps, 
 
    { 
 
    handleOpenModal, 
 
    handleCloseModal, 
 
    handleGoBack, 
 
    handleComponentPropertyChange, 
 
    handleComponentNameChange, 
 
    handleComponentTypeChange, 
 
    handleSubmit 
 
    } 
 
)(AddFormComponent)

addFormComponentReducer.js

import _ from 'lodash' 
 
import { 
 
    ADD_FORM_COMPONENT_TOGGLE_MODAL, 
 
    ADD_FORM_COMPONENT_CLOSE_MODAL, 
 
    ADD_FORM_COMPONENT_GO_BACK, 
 
    ADD_FORM_COMPONENT_SUBMIT, 
 
    ADD_FORM_COMPONENT_PROPERTY_CHANGE, 
 
    ADD_FORM_COMPONENT_PROPERTY_ERROR, 
 
    ADD_FORM_COMPONENT_KEY_ERROR, 
 
    ADD_FORM_COMPONENT_NAME_CHANGE, 
 
    ADD_FORM_COMPONENT_NAME_ERROR, 
 
    ADD_FORM_COMPONENT_TYPE_CHANGE, 
 
    ADD_FORM_COMPONENT_TYPE_ERROR 
 
} from '../actions/addFormComponentActions' 
 

 
let initialState = { 
 
    currentStep: 1, 
 
    modalOpen: false, 
 
    component: { 
 
    key: '', 
 
    label: '', 
 
    headingText: '', 
 
    text: '' 
 
    }, 
 
    errors: { 
 
    key: { 
 
     hasError: false, 
 
     msg: '' 
 
    }, 
 
    label: { 
 
     hasError: false, 
 
     msg: '' 
 
    }, 
 
    text: { 
 
     hasError: false, 
 
     msg: '' 
 
    } 
 
    } 
 
} 
 

 
function addFormComponentReducer(state = initialState, action) { 
 
    switch (action.type) { 
 
    case ADD_FORM_COMPONENT_TOGGLE_MODAL: 
 
     return { 
 
     ...state, 
 
     modalOpen: action.payload.isOpen, 
 
     currentStep: 1 
 
     } 
 
    case ADD_FORM_COMPONENT_CLOSE_MODAL: 
 
     return initialState; 
 
    case ADD_FORM_COMPONENT_GO_BACK: 
 
     return { 
 
     ...state, 
 
     currentStep: 1 
 
     } 
 
    case ADD_FORM_COMPONENT_SUBMIT: 
 
     return initialState; 
 
    case ADD_FORM_COMPONENT_PROPERTY_CHANGE: 
 
     return { 
 
     ...state, 
 
     component: { 
 
      ...state.component, 
 
      [action.payload.key]: action.payload.value 
 
     }   
 
     } 
 
    case ADD_FORM_COMPONENT_PROPERTY_ERROR: 
 
     return { 
 
     ...state, 
 
     errors: { 
 
      ...state.errors, 
 
      [action.payload.key]: { 
 
      hasError: action.payload.hasError, 
 
      msg: action.payload.msg 
 
      } 
 
     }   
 
     } 
 
    case ADD_FORM_COMPONENT_TYPE_CHANGE: 
 
     return { 
 
     ...state, 
 
     componentType: action.payload.componentType, 
 
     currentStep: 2 
 
     } 
 
    default: 
 
     return state 
 
    } 
 
} 
 

 
export default addFormComponentReducer

index.js - のReduを組み合わせますCERの

import { combineReducers } from 'redux' 
 

 
//import FormEngine reducers 
 
import formReducer from './formReducer' 
 
//import addFormComponentReducer from './addFormComponentReducer' 
 
import componentEditorReducer from './componentEditorReducer' 
 

 
const rootFormEngineReducer = combineReducers({ 
 
    form: formReducer, 
 
    //addComponent: addFormComponentReducer, 
 
    componentEditor: componentEditorReducer 
 
}) 
 

 
export default rootFormEngineReducer

rootReducer.js

import { combineReducers } from 'redux' 
 

 
//import reducers 
 
import rootCoreLayoutReducer from '../features/CoreLayout/reducers' 
 
import rootFormEngineReducer from '../features/FormEngine/reducers' 
 
import addComponentReducer from '../features/FormEngine/reducers/addFormComponentReducer' 
 

 
const rootReducer = combineReducers({ 
 
    coreLayout: rootCoreLayoutReducer, 
 
    formEngine: rootFormEngineReducer, 
 
    addComponent: addComponentReducer 
 
}) 
 

 
export default rootReducer

+0

コンポーネントのツリー構造とは何ですか?モーダルコンポーネントは、サイズと反応グリッドレイアウトの兄弟ですか? – rauliyohmc

+0

はい、私はそれが兄弟であると信じています...あなたが求めているのかどうかは分かりませんが、いくつかのコードを追加しました。だから、AddFormComponentがあります。それは開いたModalです。 LayoutComponentには入れ子になったComponents/LayoutComponentsを含めることができるため、各「LayoutComponent」内にも表示されます( –

+0

)。 'PureComponents'や' shouldComponentUpdate'を自由に使用していますか?各コンポーネントに必要な小道具を_only_で渡していますか(つまり、一般的な状態スプレッドはありません)? – monners

答えて

2

あなたが任意のパフォーマンスの最適化は、(shouldComponentUpdateを使用して)手動で処理する必要があり、純粋なコンポーネントを使用している場合。あなたはreduxを使用しているので、あなたのためにそれを処理することができます。しかし、あなたはそれを還元店に "接続"しなければなりません。

あなたが使用することを選択した場合はReduxの、特にあなたのケースでモーダルの可視性を他の特性に関連していないことを保証接続:

modalOpenはformEngineにネストされています。それがformEngineをリッスンする他のものを変更するとき

+0

答えをありがとうございます。しかし、私はまだPerfログで同じ結果を得ています(それはまだSizeMe /反応グリッドレイアウトを再描画します)。このコンポーネントのコードを上に追加しました。これはSizeMeの検出と関係がありますか幅の変更?ブラウザにスクロールバーが追加されていなくても、まだレンダリングされていないので、恐らくそうではありません。 –

+0

一見、 "modalOpen"を小道具として渡しているようです。親コンポーネントはこの値を渡す責任があるので、modalOpenの変更により、親を再レンダリングさせるのは、それを小道具を通過させなければならないためです。とにかくそれは私の推測です。あなたのモーダルディレクティを還元店に接続してみてください。 –

+0

私は接続するために渡していたmapStateToPropsメソッドのプロパティもハードコーディングしていました。私はそれを少しやって遊んだが、それも問題を引き起こしていたようだ。なぜ状態が変わっていないのか、私がtherから小道具を取り除いて減速機の初期状態にすると、モーダルを開くとレイアウトコンポーネントが再レンダリングを停止したのです。 –

関連する問題