New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configuring screen options based on props and state #24
Comments
There's nothing hacky about communicating with params, params are basically props/state for the header.
It's not more complicated than it needs to be. navigationOptions are static because some of them need to be read statically. For instance
Since HOCs are often applied using a decorator, how about a @navigationOptions(({navigation: {params}}) => ({
title: `${params.user}'s Profile`,
}))
@myHOC
class MyRoute extends PureComponent {
// ...
}
|
Yes, params are props for the route. It's hacky because of the way we need to use them to achieve basic things.
I have seen people pass instance methods, and sometimes entire component because it's too hard to do basic things with the current API. #145 is a great example of several things people are doing because of the limitations. Take a look at that and tell me it's not hacky. It probably seems easy for you because you use Redux. Not everyone uses Redux. And even when you use Redux, some use cases still need weird code to be addressed e.g. side-effects and when local state is involved.
I strongly disagree. Literally anyone I talked to who needs to do any of the stuff I listed above has told me otherwise.
Sure, this is an addition to the API. For navigators which need static configuration to work, can enforce them by throwing a descriptive error. The primary goal of this is to make it easier to update the options dynamically. They can still be initialized statically, either with a static As an unrelated note, it probably makes sense to also render all screens in drawer navigator by default similar to
The issue with HOCs is probably the least important one and can be easily fixed. I mentioned is because it's a side-effect the API. A different API using an HOC/decorator is still not ideal and easy to incorrectly use since it relies on the order in which HOC/decorators are applied. |
I used React Navigation before I used Redux, I only use Redux for a subset of things (global state that was already global state before I used Redux), and I have yet to use Redux in any navigationOptions code. Side topic: I don't even know of a way to access Redux from |
I'll agree that the idea of being able to set some 100% active route only options from the instance would look nice, namely: this.props.navigation.setOptions({
headerRight: // ... my buttons
}); However I do believe that there are solutions to the issue of needing to use
I don't like how this can result in some code do being non-DRY. i.e. If I want to make my title vary based on state but I still need a static // Static navigationOptions
navigationOptions = {
title: 'My Profile',
};
// Instance
this.props.navigation.setOptions({
title: this.state.isEditing ? 'Editing My Profile' : 'My Profile',
}); In this example I can just move
params are not header state, they are route params/state. You should not need 2 sources of truth. If you have a piece of state that is relevant to the header, say
I was going to say I don't like the fact that this means all my drawer routes that do async initialization on mount would now have to register focus handlers and implement complex "Is this the first focus, or am I already initialized" handling just to avoid every inactive drawer route from slowing down app-startup and fetching data that will be out of data if the user ever switches. But I suppose that the fact the I see 3 possible options/solutions to our problems:
|
It won't override, it will shallow merge them, similar to
ex-navigation has it, I don't remember exactly, but it can't be the same event API. there's no way to manage a subscription inside
No reason you can't use a constant for those rare occasions. It's not much different from initializing state in constructor and then updating it later with
It's just a boolean check, nothing complex. And it can be abstracted out to an HOC to reduce boilerplate. Anyway, we can probably introduce a bunch of new API and new patterns to solve the problem we encounter. But we are writing React apps and the more we make the library play with React, the easier we make it to learn and use. |
Came of with another API when talking to @brentvatne about it function App() {
return (
<Screen
options={{
title: 'Hello'
}}
render={({ navigation }) => {
return <Text>Hello world</Text>
}}
/>
);
} With this, static |
@satya164 How do you intend to communicate with the header? Do you just mean that the header component is inside the It's an interesting idea, but keep in mind that sometimes Header is shared between routes instead of being present inside the Route's card. This is the case on iOS and possibly the case if you are trying to implement Material Design style shared element transitions on Android like you see in Google's apps (try Inbox for example). We also still need something for DrawerNavigator and TabNavigator where options are used in shared elements (drawer sidebar and tab labels) outside of the route. ^_^ This kind of thing might be easier if there was some way React's Portals would let you create a Portal inside React instead of just a Portal to a dom node. |
Header can be anywhere, doesn't have to be inside |
Ok. Though I don't see how it's different from |
This component can be rendered all the time, but the screen content doesn't have to be. |
issue that would be solved easily by this: react-navigation/react-navigation#940 |
I second the API presented in previous comment. I've been partially prototyping this out in my free time recently, thanks to @ericvicenti pointing me towards that direction. We had this idea of making it easy to just render a class HomeScreen extends React.Component({
render() {
return (
<Screen header={<Header title="Welcome" />}>
{...screenContent}
</Screen>
);
}
}); This allows for easy customization of the header. If the header is not set on Screen, it will not be rendered at all. It also makes it easy for people to connect their state with the header which I guess has been one of the most common requests.
As @satya164 mentioned, this can be done with a similar to React's portal pattern - effectively, reusing the existing implementation of the header. However, I've been thinking to what degree we could achieve native-like look&feel of the header by having it separate on each screen and figuring out a new way for shared-element transitions. |
Yeah, I think its a good idea to move the library in this direction. We need a nicer way of dynamic option configuration, and the convention here is rendering. But we should take an incremental approach, so the only thing that makes sense as the first step is @satya164, I'm curious how a component with Also I'd like to suggest that we have separate option config components and prop types for each navigator. That way we can implement cute things like this: const HomeScreen = props =>
<React.Fragment>
<StackHeader
title="Welcome"
rightComponent={<MyButton />}
/>
<HomeScreenContent />
<TabBarItem icon={Icons.home} label="Home"/>
<React.Fragment> |
@ericvicenti So the majority of navigator options that affect the UI (including the titles for headers, tabs, and drawers) get moved into render components and disappear from static navigationOptions? I suppose we could even kill
Just need to contemplate whether this may work with the iOS back title behaviour. |
My idea was to shallow merge the stuff from
This one can support all use cases without a static const HomeScreen = props =>
<Screen
header={<Header title="Welcome" />}
render={() => {
return <Text>Hello world</Text>
}}
/> |
Yeah, with the above example, we can easily set |
Don't forget that navigationOptions config isn't exactly defined statically if it's a function. You're not merging setOptions with a single unchanging object. You're merging it with the result of a function that is re-executed whenever params change. |
Yeah, this is what has me confused. Seems like we will might momentarily show incorrect options on every props change because we get them from the static function and also in the options component |
We'll probably have to store the result of Or we could just dive straight into your StackHeader/TabBarItem/etc component API and provide some backwards compatibility by checking navigationOptions in the navigator when the dynamic option has not been set. |
@satya164 Is suggesting that this fixes the issue of context being inaccessible in Is this an accepted conclusion? If so then we are going to have to make sure that whatever solution we choose allows static properties such as |
@dantman The solution we proposed in #24 (comment) should not require any static options since it allows to defer rendering of the screen to when necessary. It also works well with context and other patterns. |
@satya164 Sure, I'm just mentioning it because we have around 3 different proposals, some that still include static options, and don't seem to have consensus on exactly which one we want. And we're still talking about how to merge |
I talked to @satya164 earlier today about getting a reference implementation in place and I am thinking I'll give it a go in my free time this week. |
I just add my 2 cents here against
I love react-navigation and its way better than native navigation but I agree with @satya164 that this is pattern is more hacky… EDITED: To work around this issue, redux actually come to the rescue, by hoisting the state all the way to redux, I can use the |
its really simple you can make stateless component and connect it with redux like this :
and then use it in static options :
|
You Can call SetState or any other function inside your component from static navigationOptions using this simple lines
Hope this may help someone. |
One year has completed since this RFC opened. Is any progress made on how to handle headerOptions using state directly?. Dynamically configuring NavigationOptions from screen state is one of the major bottleneck of this Library. @brentvatne @satya164 @ericvicenti @grabbou Pls have a fresh look into this. |
we are not working on this right now. we don't need a reminder of how much time has passed since this discussion started, it's here so that people can comment on it and provide their insights / ideas, and when someone wants to work on it, then it can be turned into a proper rfc and executed on. |
Any update? |
Brilliant! Thanks! This help me to change the state of the modal visibility |
@toto104 You are Welcome, Use it wherever you like 😃 . |
Currently the screen options can be specified statically. If you need to configure any options based on props and state of the component, or want to update state and props based on some action such as tab press, you need to do it in a hacky way by changing params. it's way more complicated than it needs to be. It also breaks when used with HOCs which don't hoist static props, a common source of confusion. (#145, #2725, #2804, #2955)
Proposal
We can add an additional method
setOptions
on thenavigation
prop:If you call
setOptions
again at some point, the object is shallow merged with previous configuration. It might seem a little ugly at first, but this imperative API allows us to build a declarative API on top of it:NavigationOptions
is a React component that can be used to configure the current screen from the render method. It's very useful when your screen's configuration depends on the props, state or both. (#2546)The static way of specifying the config can still be supported for simplicity and backward compatibility.
The text was updated successfully, but these errors were encountered: