{"id":22689619,"url":"https://github.com/idkjs/headerbuttonscreeninteraction","last_synced_at":"2026-05-04T12:31:28.808Z","repository":{"id":92960290,"uuid":"204582265","full_name":"idkjs/HeaderButtonScreenInteraction","owner":"idkjs","description":"Demonstration Header Button in Reason React Native Navigation with Unstated","archived":false,"fork":false,"pushed_at":"2019-08-27T05:49:34.000Z","size":173,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-27T17:51:22.944Z","etag":null,"topics":["bucklescript","expo","react-native","react-navigation","reason-react-native","reason-react-navigation","reasonml","unstated"],"latest_commit_sha":null,"homepage":"https://expo.io/@idkjs/ReasonHeaderScreenDemo","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/idkjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-08-27T00:00:33.000Z","updated_at":"2019-08-27T00:41:45.000Z","dependencies_parsed_at":"2023-08-23T22:46:08.326Z","dependency_job_id":null,"html_url":"https://github.com/idkjs/HeaderButtonScreenInteraction","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/idkjs/HeaderButtonScreenInteraction","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idkjs%2FHeaderButtonScreenInteraction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idkjs%2FHeaderButtonScreenInteraction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idkjs%2FHeaderButtonScreenInteraction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idkjs%2FHeaderButtonScreenInteraction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/idkjs","download_url":"https://codeload.github.com/idkjs/HeaderButtonScreenInteraction/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/idkjs%2FHeaderButtonScreenInteraction/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32607321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"ssl_error","status_checked_at":"2026-05-04T10:08:02.005Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bucklescript","expo","react-native","react-navigation","reason-react-native","reason-react-navigation","reasonml","unstated"],"created_at":"2024-12-10T00:20:37.473Z","updated_at":"2026-05-04T12:31:28.803Z","avatar_url":"https://github.com/idkjs.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Header Interaction with Screen Component](https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component)\n\n## See the result at \u003chttps://expo.io/@idkjs/ReasonHeaderScreenDemo\u003e\n\nThis is the `js` version from the docs at [here](https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component)\n\n```js\nclass HomeScreen extends React.Component {\n  static navigationOptions = ({ navigation }) =\u003e {\n    return {\n      headerTitle: \u003cLogoTitle /\u003e,\n      headerRight: (\n        \u003cButton\n          onPress={navigation.getParam('increaseCount')}\n          title=\"+1\"\n          color=\"#fff\"\n        /\u003e\n      ),\n    };\n  };\n\n  componentDidMount() {\n    this.props.navigation.setParams({ increaseCount: this._increaseCount });\n  }\n\n  state = {\n    count: 0,\n  };\n\n  _increaseCount = () =\u003e {\n    this.setState({ count: this.state.count + 1 });\n  };\n\n  /* later in the render function we display the count */\n}\n```\n\nIn the ReasonML version I could not figure out how to use share the value of the `HomeScreen` state with the incrementing button module. I took a hint from the doc which say:\n\n\u003e React Navigation doesn't guarantee that your screen component will be mounted before the header. Because the increaseCount param is set in componentDidMount, we may not have it available to us in navigationOptions. This usually will not be a problem because onPress for Button and Touchable components will do nothing if the callback is null. If you have your own custom component here, you should make sure it behaves as expected with null for its press handler prop.\n\n\u003e As an alternative to setParams, you could use a state management library (such as Redux or MobX) and communicate between the header and the screen in the same way you would with two distinct components.\n\nSo I figured I need some sort of state management solution which I have never used at this point. I remembered [@ken_wheeler] talking about how great [unstated](https://github.com/jamiebuilds/unstated-next) was so I checked github to see if anyone had tried it in `react-native`. I found [@@mirshko](https://mobile.twitter.com/mirshko)'s [unleaded repo](https://github.com/mirshko/unleaded) and go the guidance I needed. While looking around for that I found [re-unstated-next](https://github.com/Raincal/re-unstated-next/blob/master/example/Index.re) by [Yujia](https://mobile.twitter.com/cyj228) which implements `unstated` in ReasonML. You can find that implemented [`src/UnstatedDemo.re`](\"./src/UnstatedDemo.re\").\n\nAnyway, here is how I got the `state` shared using a library as suggested by the docs. I created the `CounterState.re` module to house my counter's state.\n\n```reason\n// straight from https://github.com/Raincal/re-unstated-next/blob/master/example/Index.re\ntype counter = {\n  count: int,\n  decrement: unit =\u003e unit,\n  increment: unit =\u003e unit,\n};\n\nlet useState = initial =\u003e {\n  React.useReducer((_, action) =\u003e action, initial);\n};\n\nlet useCounter = (~initialState=0, ()) =\u003e {\n  let (count, setCount) = useState(initialState);\n  let decrement = () =\u003e setCount(count - 1);\n  let increment = () =\u003e setCount(count + 1);\n  {count, decrement, increment};\n};\n\nmodule Counter =\n  UnstatedNext.CreateContainer({\n    type state = int;\n    type value = counter;\n    let useHook = useCounter;\n  });\n```\n\nIn `HomeScreen` module I opened that module with `open CounterState` the created an `IncButton` module for the button I want to pass to `headerRight` in `NavigationOptions` in the `HomeScreen` module. I did this because I need to use `CounterStates` `useContainer` function and didn't want to figure out passing directly in `headerRight`. I then passed the `IncButton` to `headerRight`.\n\nThis is the `IncButton` that goes in the header to the right and accesses the state container.\n\n```reason\nmodule IncButton = {\n  [@react.component]\n  let make = () =\u003e {\n    let counter = Counter.useContainer();\n    \u003cButton title=\"+\" color=\"#fff\" onPress={_ =\u003e counter.increment()} /\u003e;\n  };\n};\n```\n\n```reason\n  make-\u003eNavigationOptions.(setNavigationOptions(t(\n    // ~title=\"Home\",\n    // headerTitle instead of title\n      ~headerTitle=NavigationOptions.HeaderTitle.element(\u003cLogoTitle /\u003e),\n      ~headerRight=\u003cButton title=\"Info\"\n        color=\"#fff\" onPress={_e =\u003e\n          Alert.alert(~title=\"This is a button!\", ());\n        }\n      /\u003e,\n     ())));\n```\n\nThen in the `HomeScreen` module we render the current count in the ui with this line where access the count value on `Counter.useContainer`.\n\n```reason\n...\n\u003cText\u003e\n  {\"Count: \" ++ string_of_int(Counter.useContainer().count) |\u003e React.string}\n\u003c/Text\u003e\n...\n```\n\nThe trick to getting this all to work is to make sure that all these modules have access to the state container. I do that by wrapping my reactnavigation `AppContainer` in `\u003cContainer.Provider\u003e`, the state container;s provider.\n\n```reason\n@react.component]\nlet make = () =\u003e\n  \u003cCounter.Provider initialState=0\u003e \u003cAppContainer /\u003e \u003c/Counter.Provider\u003e;\n```\n\nThis is what it looks like:\n\n![header-button-screen-interaction](./header-button-interaction.gif)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidkjs%2Fheaderbuttonscreeninteraction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fidkjs%2Fheaderbuttonscreeninteraction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fidkjs%2Fheaderbuttonscreeninteraction/lists"}