reactstrap を使ったコンポーネントのレンダリング制御

GROWIbootstrap を用いスタイルを制御しています。
基本的にはバニラな bootstrap を用い、なるべく reactstrap を用いないのが GROWI チームの方針です。

Bootstrap 4 化に際し、通知設定のページを React で構築することになりました。

該当のPR external_link

バニラな bootstrap を用いた実装

NotificationSetting.jsx
render() { return ( <React.Fragment> <div className="notification-settings"> <ul className="nav nav-tabs" role="tablist"> <li className="active"> <a href="#slack-configuration" data-toggle="tab" role="tab"><i className="icon-settings"></i> Slack Configuration</a> </li> <li> <a href="#user-trigger-notification" data-toggle="tab" role="tab"><i className="icon-settings"></i> User Trigger Notification</a> </li> <li> <a href="#global-notification" data-toggle="tab" role="tab"><i className="icon-settings"></i> Global Notification</a> </li> </ul> <div className="tab-content m-t-15"> <div id="slack-configuration" className="tab-pane active" role="tabpanel"> <SlackAppConfiguration /> </div> <div id="user-trigger-notification" className="tab-pane" role="tabpanel"> <UserTriggerNotification /> </div> <div id="global-notification" className="tab-pane" role="tabpanel"> <GlobalNotification /> </div> </div> </div> </React.Fragment> ); }

しかし、この実装だと各タブ内のコンポーネントがレンダリングしてしまいます。
問題点としてはタブを開いていないのに、データを取得してしまいます。
そのため、タブを開いた時にレンダリングする実装に変更する必要があります。

reactstrap を用いた実装

NotificationSetting.jsx
class NotificationSetting extends React.Component { constructor() { super(); this.state = { activeTab: 'slack-configuration', // Prevent unnecessary rendering activeComponents: new Set(['slack-configuration']), }; this.toggleActiveTab = this.toggleActiveTab.bind(this); } ~中略~ toggleActiveTab(activeTab) { this.setState({ activeTab, activeComponents: this.state.activeComponents.add(activeTab), }); } render() { const { activeTab, activeComponents } = this.state; return ( <React.Fragment> <Nav tabs> <NavItem> <NavLink className={`${activeTab === 'slack-configuration' && 'active'} `} onClick={() => { this.toggleActiveTab('slack-configuration') }} > <i className="icon-settings"></i> Slack Configuration </NavLink> </NavItem> <NavItem> <NavLink className={`${activeTab === 'user-trigger-notification' && 'active'} `} onClick={() => { this.toggleActiveTab('user-trigger-notification') }} > <i className="icon-settings"></i> User Trigger Notification </NavLink> </NavItem> <NavItem> <NavLink className={`${activeTab === 'global-notification' && 'active'} `} onClick={() => { this.toggleActiveTab('global-notification') }} > <i className="icon-settings"></i> Global Notification </NavLink> </NavItem> </Nav> <TabContent activeTab={activeTab}> <TabPane tabId="slack-configuration"> {activeComponents.has('slack-configuration') && <SlackAppConfiguration />} </TabPane> <TabPane tabId="user-trigger-notification"> {activeComponents.has('user-trigger-notification') && <UserTriggerNotification />} </TabPane> <TabPane tabId="global-notification"> {activeComponents.has('global-notification') && <GlobalNotification />} </TabPane> </TabContent> </React.Fragment> ); } }

現在開いているタブと既にレンダーしたコンポーネントを state として管理します。
なので各コンポーネントの render が走るのは 1 回目にタブを開いた時だけです。
タブを切り替えても再度レンダリングされることはありません。

activeComponents は配列ではなく Set オブジェクト を用いているため、値が重複することはありません。