{"action":"created","issue":{"url":"https://api.github.com/repos/reflux/refluxjs/issues/422","labels_url":"https://api.github.com/repos/reflux/refluxjs/issues/422/labels{/name}","comments_url":"https://api.github.com/repos/reflux/refluxjs/issues/422/comments","events_url":"https://api.github.com/repos/reflux/refluxjs/issues/422/events","html_url":"https://github.com/reflux/refluxjs/issues/422","id":109860702,"number":422,"title":"How to communicate an action to a React component","user":{"login":"jfairley","id":992483,"avatar_url":"https://avatars.githubusercontent.com/u/992483?v=3","gravatar_id":"","url":"https://api.github.com/users/jfairley","html_url":"https://github.com/jfairley","followers_url":"https://api.github.com/users/jfairley/followers","following_url":"https://api.github.com/users/jfairley/following{/other_user}","gists_url":"https://api.github.com/users/jfairley/gists{/gist_id}","starred_url":"https://api.github.com/users/jfairley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jfairley/subscriptions","organizations_url":"https://api.github.com/users/jfairley/orgs","repos_url":"https://api.github.com/users/jfairley/repos","events_url":"https://api.github.com/users/jfairley/events{/privacy}","received_events_url":"https://api.github.com/users/jfairley/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/reflux/refluxjs/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2015-10-05T18:45:41Z","updated_at":"2015-10-13T07:07:50Z","closed_at":null,"body":"I asked this question [on StackOverflow](http://stackoverflow.com/questions/32340399/how-to-communicate-an-action-to-a-react-component), but it's not getting much traction. I'm hoping someone here will have an opinion... Sorry in advance for the length.\r\n\r\n<br>\r\n\r\nWhile my scenario is pretty specific, I think it speaks to a bigger question in Flux. Components should be simple renderings of data from a store, but what if your component renders a third-party component which is stateful? How does one interact with this third-party component while still obeying the rules of Flux?\r\n\r\nSo, I have a React app that contains a video player (using [clappr](https://github.com/clappr/clappr)). A good example is seeking. When I click a location on the progress bar, I want to seek the video player. Here is what I have right now. I've tried to strip down my code to the most relevant parts.\r\n\r\n```javascript\r\n    var PlayerActions = Reflux.createActions([\r\n        'seek'\r\n    ]);\r\n    \r\n    var PlayerStore = Reflux.createStore({\r\n        listenables: [\r\n            PlayerActions\r\n        ],\r\n    \r\n        onSeek(seekTo) {\r\n            this.data.seekTo = seekTo;\r\n            this.trigger(this.data);\r\n        }\r\n    });\r\n    \r\n    var Player = React.createClass({\r\n        mixins: [Reflux.listenTo(PlayerStore, 'onStoreChange')],\r\n    \r\n        onStoreChange(data) {\r\n            if (data.seekTo !== this.state.seekTo) {\r\n                window.player.seek(data.seekTo);\r\n            }\r\n    \r\n            // apply state\r\n            this.setState(data);\r\n        }\r\n\r\n        componentDidMount() {\r\n            // build a player\r\n            window.player = new Clappr.Player({\r\n                source: this.props.sourcePath,\r\n                chromeless: true,\r\n                useDvrControls: true,\r\n                parentId: '#player',\r\n                width: this.props.width\r\n            });\r\n        },\r\n\r\n        componentWillUnmount() {\r\n            window.player.destroy();\r\n            window.player = null;\r\n        },\r\n\r\n        shouldComponentUpdate() {\r\n            // if React realized we were manipulating DOM, it'd certainly freak out\r\n            return false;\r\n        },\r\n\r\n        render() {\r\n            return <div id='player'/>;\r\n        }\r\n    });\r\n```\r\n\r\nThe bug I have with this code is when you try to seek to the same place twice. Imagine the video player continuously playing. Click on the progress bar to seek. Don't move the mouse, and wait a few seconds. Click on the progress bar again on the same place as before. The value of `data.seekTo` did not change, so `window.player.seek` is not called the second time.\r\n\r\n\r\nI've considered a few possibilities to solve this, but I'm not sure which is more correct. Input requested...\r\n\r\n<br>\r\n\r\n___1: Reset `seekTo` after it is used___\r\n\r\nSimply resetting `seekTo` seems like the simplest solution, though it's certainly no more elegant. Ultimately, this feels more like a band-aid.\r\n\r\nThis would be as simple as ...\r\n\r\n```javascript\r\n    window.player.on('player_seek', PlayerActions.resetSeek);\r\n```\r\n\r\n<br>\r\n\r\n___2: Create a separate store that acts more like a pass-through___\r\n\r\nBasically, I would listen to a `SeekStore`, but in reality, this would act as a pass-through, making it more like an action that a store. This solution feels like a hack of Flux, but I think it would work.\r\n\r\n\r\n```javascript\r\n    var PlayerActions = Reflux.createActions([\r\n        'seek'\r\n    ]);\r\n    \r\n    var SeekStore = Reflux.createStore({\r\n        listenables: [\r\n            PlayerActions\r\n        ],\r\n    \r\n        onSeek(seekTo) {\r\n            this.trigger(seekTo);\r\n        }\r\n    });\r\n    \r\n    var Player = React.createClass({\r\n        mixins: [Reflux.listenTo(SeekStore, 'onStoreChange')],\r\n    \r\n        onStoreChange(seekTo) {\r\n            window.player.seek(seekTo);\r\n        }\r\n    });\r\n```\r\n\r\n<br>\r\n\r\n___3: Interact with `window.player` within my actions___\r\n\r\nWhen I think about it, this _feels_ correct, since calling `window.player.seek` is in fact an action. The only weird bit is that I don't feel right interacting with `window` inside the actions. Maybe that's just an irrational thought, though.\r\n\r\n\r\n```javascript\r\n    var PlayerActions = Reflux.createActions({\r\n        seek: {asyncResult: true}\r\n    });\r\n    PlayerActions.seek.listen(seekTo => {\r\n        if (window.player) {\r\n            try {\r\n                window.player.seek(seekTo);\r\n                PlayerActions.seek.completed(err);\r\n            } catch (err) {\r\n                PlayerActions.seek.failed(err);\r\n            }\r\n        } else {\r\n            PlayerActions.seek.failed(new Error('player not initialized'));\r\n        }\r\n    });\r\n```\r\n\r\nBTW, there's a whole other elephant in the room that I didn't touch on. In all of my examples, the player is stored as `window.player`. Clappr did this automatically in older versions, but though it has since been fixed to work with Browserify, we continue to store it on the `window` (tech debt). Obviously, my third solution is leveraging that fact, which it technically a bad thing to be doing. Anyway, before anyone points that out, understood and noted.\r\n\r\n<br>\r\n\r\n___4: Seek via `dispatchEvent`___\r\n\r\nI also understand that dispatching a custom event would get the job done, but this feels way wrong considering I have Flux in place. This feels like I'm going outside of my Flux architecture to get the job done. I should be able to do it and stay inside the Flux playground.\r\n\r\n```javascript\r\n    var PlayerActions = Reflux.createActions({\r\n        seek: {asyncResult: true}\r\n    });\r\n    PlayerActions.seek.listen(seekTo => {\r\n        try {\r\n            let event = new window.CustomEvent('seekEvent', {detail: seekTo});\r\n            window.dispatchEvent(event);\r\n            PlayerActions.seek.completed(err);\r\n        } catch (err) {\r\n            PlayerActions.seek.failed(err);\r\n        }\r\n    });\r\n    \r\n    var Player = React.createClass({\r\n        componentDidMount() {\r\n            window.addEventListener('seekEvent', this.onSeek);\r\n        },\r\n        componentWillUnmount() {\r\n            window.removeEventListener('seekEvent', this.onSeek);\r\n        },\r\n        onSeek(e) {\r\n            window.player.seek(e.detail);\r\n        }\r\n    });\r\n```"},"comment":{"url":"https://api.github.com/repos/reflux/refluxjs/issues/comments/147625706","html_url":"https://github.com/reflux/refluxjs/issues/422#issuecomment-147625706","issue_url":"https://api.github.com/repos/reflux/refluxjs/issues/422","id":147625706,"user":{"login":"spoike","id":9253,"avatar_url":"https://avatars.githubusercontent.com/u/9253?v=3","gravatar_id":"","url":"https://api.github.com/users/spoike","html_url":"https://github.com/spoike","followers_url":"https://api.github.com/users/spoike/followers","following_url":"https://api.github.com/users/spoike/following{/other_user}","gists_url":"https://api.github.com/users/spoike/gists{/gist_id}","starred_url":"https://api.github.com/users/spoike/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spoike/subscriptions","organizations_url":"https://api.github.com/users/spoike/orgs","repos_url":"https://api.github.com/users/spoike/repos","events_url":"https://api.github.com/users/spoike/events{/privacy}","received_events_url":"https://api.github.com/users/spoike/received_events","type":"User","site_admin":false},"created_at":"2015-10-13T07:07:50Z","updated_at":"2015-10-13T07:07:50Z","body":"Alternatively you could have two components: `Player` and `PlayerButtons`, the latter taking `Player` as prop from a parent component so it can wire itself up to the player events. Here is an example of using both in the same parent's `render` method:\r\n\r\n```JSX\r\nrender: () => {\r\n  var player = <Player />;\r\n  return (\r\n    <ParentBase>\r\n      <PlayerButtons player={player}></PlayerButtons>\r\n      <something else=\"lol\" /><hr />\r\n      { player } /* here be the player */\r\n    </ParentBase>\r\n  );\r\n}\r\n```\r\n\r\nYou can use [`propTypes` to validate](https://facebook.github.io/react/docs/reusable-components.html) the player prop in PlayerButtons:\r\n\r\n```JSX\r\n// assuming PlayerButtons is an ES6 Class\r\nPlayerButtons.propTypes = {\r\n  player: React.PropTypes.element // or shape if you want to be more specific with callbacks\r\n};\r\n```"}}