|  |  |  | import React from 'react'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface Props { | 
					
						
							|  |  |  |   /** The View class, which will be instantiated then treated like a Backbone View */ | 
					
						
							|  |  |  |   readonly View: BackboneViewConstructor; | 
					
						
							|  |  |  |   /** Options to be passed along to the view when constructed */ | 
					
						
							|  |  |  |   readonly options: object; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface BackboneView { | 
					
						
							|  |  |  |   remove: () => void; | 
					
						
							|  |  |  |   render: () => void; | 
					
						
							|  |  |  |   el: HTMLElement; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface BackboneViewConstructor { | 
					
						
							|  |  |  |     new (options: object): BackboneView; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Allows Backbone Views to be rendered inside of React (primarily for the Style Guide) | 
					
						
							|  |  |  |  * while we slowly replace the internals of a given Backbone view with React. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export class BackboneWrapper extends React.Component<Props, {}> { | 
					
						
							|  |  |  |   protected el: HTMLElement | null = null; | 
					
						
							|  |  |  |   protected view: BackboneView | null = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public componentWillUnmount() { | 
					
						
							|  |  |  |     this.teardown(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public shouldComponentUpdate() { | 
					
						
							|  |  |  |     // we're handling all updates manually
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public render() { | 
					
						
							|  |  |  |     return <div ref={this.setEl} />; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected setEl = (element: HTMLDivElement | null) => { | 
					
						
							|  |  |  |     this.el = element; | 
					
						
							|  |  |  |     this.setup(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected setup = () => { | 
					
						
							|  |  |  |     const { el } = this; | 
					
						
							|  |  |  |     const { View, options } = this.props; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!el) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this.view = new View(options); | 
					
						
							|  |  |  |     this.view.render(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // It's important to let the view create its own root DOM element. This ensures that
 | 
					
						
							|  |  |  |     //   its tagName property actually takes effect.
 | 
					
						
							|  |  |  |     el.appendChild(this.view.el); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected teardown() { | 
					
						
							|  |  |  |     if (!this.view) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.view.remove(); | 
					
						
							|  |  |  |     this.view = null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |