{"action":"created","issue":{"url":"https://api.github.com/repos/facebook/immutable-js/issues/370","labels_url":"https://api.github.com/repos/facebook/immutable-js/issues/370/labels{/name}","comments_url":"https://api.github.com/repos/facebook/immutable-js/issues/370/comments","events_url":"https://api.github.com/repos/facebook/immutable-js/issues/370/events","html_url":"https://github.com/facebook/immutable-js/issues/370","id":60062545,"number":370,"title":"cursorA.equals(cursorB) should check _keyPath","user":{"login":"Gozala","id":21236,"avatar_url":"https://avatars.githubusercontent.com/u/21236?v=3","gravatar_id":"","url":"https://api.github.com/users/Gozala","html_url":"https://github.com/Gozala","followers_url":"https://api.github.com/users/Gozala/followers","following_url":"https://api.github.com/users/Gozala/following{/other_user}","gists_url":"https://api.github.com/users/Gozala/gists{/gist_id}","starred_url":"https://api.github.com/users/Gozala/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Gozala/subscriptions","organizations_url":"https://api.github.com/users/Gozala/orgs","repos_url":"https://api.github.com/users/Gozala/repos","events_url":"https://api.github.com/users/Gozala/events{/privacy}","received_events_url":"https://api.github.com/users/Gozala/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-03-06T05:50:51Z","updated_at":"2015-03-15T21:51:35Z","closed_at":null,"body":"You can find more details about the issues here: https://github.com/omniscientjs/omniscient/issues/89\r\n\r\nHere is a little illustrative summary:\r\n\r\n```js\r\nlet root = Cursor.from(Immutable.fromJS({items: [{x: 3}, {x: 2}, {x: 1}]}))\r\nlet x1 = root.cursor('items').cursor(0);\r\n\r\nlet root2 = root.updateIn(['items'], items => items.reverse());\r\nlet x2 = root2.cursor('items').cursor(2);\r\n\r\nx1.equals(x2) // => true\r\nx1._keyPath === x2._keyPath // => false\r\n```\r\n\r\nNow consider react components like this:\r\n\r\n```js\r\nclass Item extends React.Component {\r\n  shouldComponentUpdate(props)  {\r\n     return !this.props.cursor.equals(props.cursor);\r\n  }\r\n  render() {\r\n    const {cursor} = this.props;\r\n    return React.DOM.div({\r\n      onClick() {\r\n         cursor.update(...)\r\n      }\r\n    }, generateContent(cursor))\r\n  }\r\n});\r\n\r\nclass App Extends React.Component {\r\n   render() {\r\n     const children = this.props.cursor.cursor('items').map(item =>\r\n        Item({ key: item.get('x'), cursor: item }));\r\n      return React.DOM.div({}, children);\r\n   }\r\n}\r\n```\r\nIf `App` is rendered with with `root` and then `root2` Item for `{x: 3}` will not re-render as equality check returns `true` even though it refers to different location in the structure. Also clicking it will end up sending changes to out of date `_keyPath`.\r\n\r\nWith react API this is actually not a big deal if one would not capture cursor in render, but in omniscient it is because cursor will be passed in into `render` itself.\r\n\r\nMaybe `.equals` behavior is correct, but what would be a way to handle such case properly ? "},"comment":{"url":"https://api.github.com/repos/facebook/immutable-js/issues/comments/81255949","html_url":"https://github.com/facebook/immutable-js/issues/370#issuecomment-81255949","issue_url":"https://api.github.com/repos/facebook/immutable-js/issues/370","id":81255949,"user":{"login":"wmertens","id":54934,"avatar_url":"https://avatars.githubusercontent.com/u/54934?v=3","gravatar_id":"","url":"https://api.github.com/users/wmertens","html_url":"https://github.com/wmertens","followers_url":"https://api.github.com/users/wmertens/followers","following_url":"https://api.github.com/users/wmertens/following{/other_user}","gists_url":"https://api.github.com/users/wmertens/gists{/gist_id}","starred_url":"https://api.github.com/users/wmertens/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wmertens/subscriptions","organizations_url":"https://api.github.com/users/wmertens/orgs","repos_url":"https://api.github.com/users/wmertens/repos","events_url":"https://api.github.com/users/wmertens/events{/privacy}","received_events_url":"https://api.github.com/users/wmertens/received_events","type":"User","site_admin":false},"created_at":"2015-03-15T21:51:35Z","updated_at":"2015-03-15T21:51:35Z","body":"@Gozala The way it works for me is because the list itself is also rendered with a cursor, and that will point to a different object even though the `item`s themselves do not change.\r\n\r\nAlso, I only interface with the store in \"container\" elements that create cursors and render \"pure\" elements.\r\n\r\nHere's my flow (I use Fluxy):\r\n`onclick() -> cursor.update() -> _onChange(newRoot) -> store saves newRoot -> notifies watchers -> redraws`\r\n\r\n`_onChange` returns undefined so the cursors remain stale. The store watchers are only set on container elements that wrap the pure elements. They simply call `this.forceUpdate()` which causes new cursors to be created from the store key they're watching.\r\n\r\nSo in fact it's `click -> new root data -> create new cursors` and then the wrapped pure elements do conditional rendering. I do check with Perf to see if there were wasted updates so I know it works as it should."}}