{"action":"created","issue":{"url":"https://api.github.com/repos/bvaughn/react-window/issues/6","repository_url":"https://api.github.com/repos/bvaughn/react-window","labels_url":"https://api.github.com/repos/bvaughn/react-window/issues/6/labels{/name}","comments_url":"https://api.github.com/repos/bvaughn/react-window/issues/6/comments","events_url":"https://api.github.com/repos/bvaughn/react-window/issues/6/events","html_url":"https://github.com/bvaughn/react-window/issues/6","id":327766528,"node_id":"MDU6SXNzdWUzMjc3NjY1Mjg=","number":6,"title":"Support just-in-time measured content","user":{"login":"bvaughn","id":29597,"node_id":"MDQ6VXNlcjI5NTk3","avatar_url":"https://avatars0.githubusercontent.com/u/29597?v=4","gravatar_id":"","url":"https://api.github.com/users/bvaughn","html_url":"https://github.com/bvaughn","followers_url":"https://api.github.com/users/bvaughn/followers","following_url":"https://api.github.com/users/bvaughn/following{/other_user}","gists_url":"https://api.github.com/users/bvaughn/gists{/gist_id}","starred_url":"https://api.github.com/users/bvaughn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bvaughn/subscriptions","organizations_url":"https://api.github.com/users/bvaughn/orgs","repos_url":"https://api.github.com/users/bvaughn/repos","events_url":"https://api.github.com/users/bvaughn/events{/privacy}","received_events_url":"https://api.github.com/users/bvaughn/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":{"login":"bvaughn","id":29597,"node_id":"MDQ6VXNlcjI5NTk3","avatar_url":"https://avatars0.githubusercontent.com/u/29597?v=4","gravatar_id":"","url":"https://api.github.com/users/bvaughn","html_url":"https://github.com/bvaughn","followers_url":"https://api.github.com/users/bvaughn/followers","following_url":"https://api.github.com/users/bvaughn/following{/other_user}","gists_url":"https://api.github.com/users/bvaughn/gists{/gist_id}","starred_url":"https://api.github.com/users/bvaughn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bvaughn/subscriptions","organizations_url":"https://api.github.com/users/bvaughn/orgs","repos_url":"https://api.github.com/users/bvaughn/repos","events_url":"https://api.github.com/users/bvaughn/events{/privacy}","received_events_url":"https://api.github.com/users/bvaughn/received_events","type":"User","site_admin":false},"assignees":[{"login":"bvaughn","id":29597,"node_id":"MDQ6VXNlcjI5NTk3","avatar_url":"https://avatars0.githubusercontent.com/u/29597?v=4","gravatar_id":"","url":"https://api.github.com/users/bvaughn","html_url":"https://github.com/bvaughn","followers_url":"https://api.github.com/users/bvaughn/followers","following_url":"https://api.github.com/users/bvaughn/following{/other_user}","gists_url":"https://api.github.com/users/bvaughn/gists{/gist_id}","starred_url":"https://api.github.com/users/bvaughn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bvaughn/subscriptions","organizations_url":"https://api.github.com/users/bvaughn/orgs","repos_url":"https://api.github.com/users/bvaughn/repos","events_url":"https://api.github.com/users/bvaughn/events{/privacy}","received_events_url":"https://api.github.com/users/bvaughn/received_events","type":"User","site_admin":false}],"milestone":null,"comments":12,"created_at":"2018-05-30T14:49:37Z","updated_at":"2018-09-26T13:14:07Z","closed_at":null,"author_association":"OWNER","body":"In order to avoid adding cost to existing list and grid components, create a new variant (e.g. `DynamicSizeList` and `DynamicSizeGrid`). This variant should automatically measure its content during the commit phase.\r\n\r\n### MVP\r\n\r\nThe initial implementation of this could work similarly to how `CellMeasurer` works in react-virtualized:\r\n1. Content is measured only if no current measurements exists.\r\n2. Measurements need to be reset externally (imperatively) if something changes.\r\n3. Cells at a given index can only be positioned after all cells before that index have been measured.\r\n\r\n### Goal\r\n\r\nThis component could perform better if we removed the third constraint above, allowing random access (by either item index or scroll offset) without measuring the preceding items. This would make react-window much more performant for use cases like chat applications.\r\n\r\nThis would also unlock the ability to use a [ResizeObserver](https://rawgit.com/WICG/ResizeObserver/master/index.html) (via [react-measure](https://github.com/souporserious/react-measure)) to automatically detect item sizing and remove the position and measurements cache entirely. This would remove the need for imperatively resetting cached measurements and dramatically improve the API.\r\n\r\nIn order for the above to be possible, the dynamic list/grid components would need to use a dramatically different approach for mapping offset to index and vice versa. ([This comment about \"scroll anchoring\" in react-virtualized](https://github.com/bvaughn/react-virtualized/issues/610#issuecomment-324890558) has some nice visuals.) Essentially, we would need to do something like this:\r\n\r\n* Estimate total size based on the number of items multiplied by a `estimatedItemSize` prop. (This estimated size won't need to be adjusted, since the mapping described below doesn't is fuzzy.) \r\n\r\n* When scroll position changes, compare the new offset to the previous offset. **If the delta is greater than some [to be determined] threshold**, set the new offset as the \"scroll anchor\". Map the offset to an estimated index (e.g. divide the offset by total estimated scrollable size and multiply that by the number of items in the collection). Store this mapped index as the \"anchor index\". For example, if the list described by the image below had 250 items, the \"anchor index\" would be 132.\r\n\r\n<img width=\"328\" alt=\"screen shot 2018-06-10 at 11 58 38 am\" src=\"https://user-images.githubusercontent.com/29597/41205298-ae8c10a4-6ca5-11e8-8630-ad94913d7112.png\">\r\n\r\n* When scroll position changes, **if the delta is less than the threshold**, choose which new items to render relative to the anchor index. Position these items relative to the previously positioned items. Continuing with the example above, if the list was scrolled by a small amount (200px) then 200px worth of additional rows would need to be appended below the previously positioned items:\r\n\r\n<img width=\"279\" alt=\"screen shot 2018-06-10 at 12 01 01 pm\" src=\"https://user-images.githubusercontent.com/29597/41205310-f760b118-6ca5-11e8-89e3-84d3f70bd3ad.png\">\r\n\r\nThe above approach has only one major downside: aligning items correctly at list boundaries. If item indices are estimated (as described above) then they likely won't line up exactly with the beginning or end of the scrollable area.\r\n* The end could potentially be accounted for by adjusting the total estimated size as the user scrolls closer to the end (although this might make scrolling feel janky).\r\n\r\n* The start of the list is harder to handle, since the first item needs to align with offset zero while still appearing to connect contiguously with items from some offset greater than zero. Perhaps another threshold could be used, a \"safe zone\", near the start of the list (e.g. if the scroll offset is less than some absolute value) which would force the list to measure all cells up to that point so they align correctly. The cost of this forced measurement would be relatively low, since it would only be a small number of items.\r\n<img width=\"317\" alt=\"screen shot 2018-06-10 at 5 04 42 pm\" src=\"https://user-images.githubusercontent.com/29597/41207796-68c6037e-6cd0-11e8-9e46-355acecbfdac.png\">\r\n\r\nThe one case that would still not be handled correctly with the above approach would be a scroll anchor that is set outside of the \"safe zone\" but a current scroll that goes inside of the safe zone (as shown below). If the user scrolls slowly back toward the beginning of the list, it may be difficult to align the first cell with zero without introducing scroll janky.\r\n<img width=\"325\" alt=\"screen shot 2018-06-10 at 5 08 26 pm\" src=\"https://user-images.githubusercontent.com/29597/41207818-ea643626-6cd0-11e8-90f3-20b59e34e8e8.png\">\r\n"},"comment":{"url":"https://api.github.com/repos/bvaughn/react-window/issues/comments/424710215","html_url":"https://github.com/bvaughn/react-window/issues/6#issuecomment-424710215","issue_url":"https://api.github.com/repos/bvaughn/react-window/issues/6","id":424710215,"node_id":"MDEyOklzc3VlQ29tbWVudDQyNDcxMDIxNQ==","user":{"login":"osdlge","id":12647729,"node_id":"MDQ6VXNlcjEyNjQ3NzI5","avatar_url":"https://avatars0.githubusercontent.com/u/12647729?v=4","gravatar_id":"","url":"https://api.github.com/users/osdlge","html_url":"https://github.com/osdlge","followers_url":"https://api.github.com/users/osdlge/followers","following_url":"https://api.github.com/users/osdlge/following{/other_user}","gists_url":"https://api.github.com/users/osdlge/gists{/gist_id}","starred_url":"https://api.github.com/users/osdlge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/osdlge/subscriptions","organizations_url":"https://api.github.com/users/osdlge/orgs","repos_url":"https://api.github.com/users/osdlge/repos","events_url":"https://api.github.com/users/osdlge/events{/privacy}","received_events_url":"https://api.github.com/users/osdlge/received_events","type":"User","site_admin":false},"created_at":"2018-09-26T13:14:07Z","updated_at":"2018-09-26T13:14:07Z","author_association":"NONE","body":"@carlosagsmendes not sure if this will help but here is what I am doing when using this to display chat history:\r\n\r\n1.) In the constructor create a ref for the list and for my `ChatHistory` component:\r\n\r\n```javascript\r\n  constructor(props) {\r\n    super(props);\r\n    this.listRef = createRef();\r\n    this.chatHistoryRef = createRef();\r\n    this.listHeight = 0;\r\n    this.state = {\r\n      chatHistory: [],\r\n      agentIsTyping: true,\r\n      context: {},\r\n    };\r\n  }\r\n```\r\n\r\nI then pass the refs down to the `ChatHistory` which is responsible for rendering the list\r\n\r\n2.) In `componentDidMount` of the parent component I use the `ChatHistory` ref to get the height of the element:\r\n\r\n```javascript\r\ncomponentDidMount() {\r\n    this.listHeight = this.chatHistoryRef.current.offsetHeight;\r\n}\r\n```\r\n\r\n3.) In the parent component I maintain an array in it's state with the chathistory details. When adding a new item to that array I do it like this:\r\n\r\n```javascript\r\n  // find out how many pixels in height the text is going to use\r\n  const size = getSize({\r\n    text: displayText,\r\n    className: styles.message,\r\n  });\r\n\r\n  let { height } = size;\r\n  height += 20; // adds some spacing in pixels between messages\r\n...rest of items needed for the chat history item..add them all to an array and update state\r\n```\r\n\r\n`getSize` is based on https://github.com/schickling/calculate-size but I modified it to support accepting a class name. That class is the same one used as the container for displaying the individual methods\r\n\r\nI'm sure there's a better way to achieve this but it seems pretty fast and I haven't hit any issues yet"}}
{"actor":{"display_login":"osdlge"}}