Created
September 19, 2015 00:34
-
-
Save bennadel/f65002a397123e026911 to your computer and use it in GitHub Desktop.
Setting The State Based On Rendered DOM Elements In ReactJS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <title> | |
| Setting The State Based On Rendered DOM Elements In ReactJS | |
| </title> | |
| <link rel="stylesheet" type="text/css" href="./demo.css"></link> | |
| </head> | |
| <body> | |
| <h1> | |
| Setting The State Based On Rendered DOM Elements In ReactJS | |
| </h1> | |
| <div id="content"> | |
| <!-- App will be rendered here. --> | |
| </div> | |
| <!-- Load scripts. --> | |
| <script type="text/javascript" src="../../vendor/reactjs/react-0.13.3.min.js"></script> | |
| <script type="text/javascript" src="../../vendor/reactjs/JSXTransformer-0.13.3.js"></script> | |
| <script type="text/jsx"> | |
| // I manage the root component. | |
| var Demo = React.createClass({ | |
| // I return the initial state of the component. | |
| getInitialState: function() { | |
| for ( var items = [], i = 1 ; i <= 50 ; i++ ) { | |
| items.push( i ); | |
| } | |
| return({ | |
| items: items, | |
| width: "full", | |
| visibleCount: 0 | |
| }); | |
| }, | |
| // --- | |
| // PUBLIC METHODS. | |
| // --- | |
| // Once the list element has been rendered, we can calculate how many items | |
| // we can render in the room allotted. | |
| calculateVisibleCount: function() { | |
| // Get the available width of the rendered element. | |
| var containerWidth = this.refs.items.getDOMNode().clientWidth; | |
| // For the sake of simplicity, we're going to hard-code the width required | |
| // to render one of the items (including the inter-item margin). | |
| var itemWidth = ( 50 + 10 ); | |
| // Set the state that will be used in the render() method to determine | |
| // how many items we can fit and how many items will have to be left in | |
| // the "+N" teaser. | |
| this.setState({ | |
| visibleCount: Math.floor( containerWidth / itemWidth ) | |
| }); | |
| }, | |
| // I get called once, on the client, when the component has been rendered | |
| // in the DOM. | |
| componentDidMount: function() { | |
| // Keep track of window-resize event since we'll have to recalculate the | |
| // number of items we can fit in the new window dimensions. | |
| window.addEventListener( "resize", this.handleWindowResize ); | |
| // Now that the DOM has been rendered, we can inspect the dimensions. | |
| this.calculateVisibleCount(); | |
| }, | |
| // I get call after changes to the virtual DOM are flushed to the physical DOM. | |
| componentDidUpdate: function() { | |
| // CAUTION: Do not try to call .setState() in this method. You will | |
| // quickly find yourself in an infinite loop. | |
| }, | |
| // I get called once right before the component is removed from the DOM. | |
| componentWillUnmount: function() { | |
| window.removeEventListener( "resize", this.handleWindowResize ); | |
| }, | |
| // I handle a click on the "set full width" link. | |
| handleFullWidth: function( event ) { | |
| this.setState( | |
| { | |
| width: "full" | |
| }, | |
| // NOTE: The second argument is a callback that will be invoked when | |
| // these state changes have been flushed to the DOM. This gives an | |
| // opportunity to update the state based on the DOM changes without | |
| // getting into an infinite loop (although, this will cause another | |
| // call to render(), which is what we want). | |
| this.calculateVisibleCount | |
| ); | |
| }, | |
| // I handle a click on the "set half width" link. | |
| handleHalfWidth: function( event ) { | |
| this.setState( | |
| { | |
| width: "half" | |
| }, | |
| // NOTE: The second argument is a callback that will be invoked when | |
| // these state changes have been flushed to the DOM. This gives an | |
| // opportunity to update the state based on the DOM changes without | |
| // getting into an infinite loop (although, this will cause another | |
| // call to render(), which is what we want). | |
| this.calculateVisibleCount | |
| ); | |
| }, | |
| // I handle window resize event. | |
| handleWindowResize: function( event ) { | |
| // Once the window is resized, it means the items container may have | |
| // changed dimensions. As such, we may have to recalculate the number | |
| // of items that can be rendered. | |
| this.calculateVisibleCount(); | |
| }, | |
| // I return the virtual DOM based on the current state. | |
| render: function() { | |
| // Determine how many items we can render. If we don't have enough | |
| // space to render all of the items, we have to account of the space | |
| // requirements of the "teaser" as well. | |
| if ( this.state.visibleCount < this.state.items.length ) { | |
| var renderCount = ( this.state.visibleCount - 1 ); | |
| var overflowCount = ( this.state.items.length - renderCount ); | |
| var teaser = ( | |
| <span key={ "teaser" } className="item teaser"> | |
| +{ overflowCount } | |
| </span> | |
| ); | |
| } else { | |
| var renderCount = this.visibleCount; | |
| var teaser = null; | |
| } | |
| // Map the items onto react elements. | |
| var items = this.state.items | |
| .slice( 0, renderCount ) | |
| .map( | |
| function operator( id ) { | |
| return( <span key={ id } className="item">{ id }</span> ); | |
| } | |
| ) | |
| ; | |
| return( | |
| <div> | |
| <p> | |
| { "Items: " } | |
| <a onClick={ this.handleFullWidth }>Full width</a> | |
| { " or " } | |
| <a onClick={ this.handleHalfWidth }>Half width</a> | |
| </p> | |
| <div ref="list" className={ ( "list " + this.state.width ) }> | |
| <div ref="items" className="items"> | |
| { items } | |
| { teaser } | |
| </div> | |
| </div> | |
| <p> | |
| Showing { renderCount } of { this.state.items.length } items. | |
| </p> | |
| </div> | |
| ); | |
| } | |
| }); | |
| // --------------------------------------------------------------------------- // | |
| // --------------------------------------------------------------------------- // | |
| // Render the root Demo and mount it inside the given element. | |
| React.render( <Demo />, document.getElementById( "content" ) ); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment