We want a statically typed scene hierarchy in such a way that when we move between nested screens, we pass state/props down and it can be updated. The state should live as close to the subtree as possible.
In other words, the view tree in a navigator looks like this:
Navigator
/ | \
Scene1 Scene2 Scene3
And in the simple optimal scenario (passing props down) we want the state tree look like this:
Navigator
|
Scene1
|
Scene2
|
Scene3
- Hierarchy: Main -> Details -> Comments
- State:
type comments = { page: int, comments: array comment };
We navigate from Details
to Comments
. The imaginary interaction is that after refreshing comments inside the comments view, the content is updated in the Details
view which is higher in the hierarchy. So in this case we want to mutate comments in the state which is shared between these two screens.
Unifying the state and view hierarchy.
Hmm, sounds familiar. Yes, React is all about it. Unfortunately we cannot use react directly because scenes are rendered as siblings.
I think the solution is to partly copy the API of reason-react.
type reduce 'action 'event = ('event => 'action) => 'event => unit;
type screen 'state 'action = {
render: 'state => reduce 'action => ReasonReact.element,
reducer: 'action => 'state => 'state,
renderChild: 'state => reduce 'action => option (screen 'childState 'childAction)
};
let comments ::project ::comments ::updateComments =>
Navigator.leafScreen
(fun () => <Comments comments project updateComments);
let details ::project => {
initialState: fun () => None,
render: fun state reduce => <Details project comments=state updateComments=(reduce (fun x => UpdateComments x)) />,
reducer: fun (action: comments) state => switch action {
| UpdateComments => {...state, comments: action}
},
renderChild: fun state reduce => switch state {
| Some x => Some (
comments ::project comments::x updateComments::(reduce (fun x => UpdateComments x)
)
| None => None
}
};
Ok, the example above sucks and is not typed correctly but you get what's going on here.
In order to render this hierarchy, the navigator recurisvely calls renderChild
.
There're no hooks like componentWillMount
because adding them would make the implementation harder and closely couple the application to the navigation lib.
I think it has a great potential in react native but it's applicable to web apps too. You could nest routes this way and then separately provide resolvers if a particular route is accessed directly.