- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Passing regular props when navigating #935
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
Comments
yeah, I was taking a look at this also. Instead of saying
you would write |
@vonovak, is that a custom function or something from react-navigation? If custom, what would that look like? |
I prefer to keep my mappings in the |
@dominicrj23 , example? |
@tmaly1980 it's a custom function that doesn't exist yet.
@dominicrj23 you'd probably need to write lots of |
@vonovak, thanks, the first example (the class generator) ALMOST worked, unfortunately, this.props.navigation is used for more than just the state, (ie setParams, navigate, etc). I changed the return line to:
This preserves this.props.navigation for other uses. |
or more general
|
@vonovak its true that you would have to write code for every screen. But most of my application use some form of redux store to get its state, and keeping the mapping in |
@vonovak and @tmaly1980: Nice workaround, works great for as far as I tested. Thanks! 👍 However, I think it would be nice if react-navigation support this out of the box. Maybe I'm trying to use this library in a way it was not meant to be used, but coupling my components this tightly to navigation feels wrong. Perhaps someone from the core team can explain why it was designed this way? And what they think about using props? |
Actually, using the class wrapper, it loses access to static navigationOptions, such as title, etc if defined in the class itself (in my case, it's a dynamic method). I've had to modify the class inheritance to extend SomeComponent instead of React.Component:
|
@dominicrj23 , don't you mean:
Since I'm using redux, I kind of like this better, but the wrapper will always work, even with redux |
@tmaly1980: Hm, you're right. I wasn't using the navigationOptions feature. Instead of having to subclass, could it be solved as follows? const paramsToProps = (SomeComponent) => {
// turns this.props.navigation.state.params into this.params.<x>
return class extends React.Component {
static navigationOptions = SomeComponent.navigationOptions;
// everything else, call as SomeComponent
render() {
const {navigation, ...otherProps} = this.props
const {state: {params}} = navigation
return <SomeComponent {...this.props} {...params} />
}
}
} |
@tmaly1980 I don't like the idea of |
To each their own, I actually am using redux, so I'm just using @dominicrj23 's suggestion. connect() and mapStateToProps are already in every place I need it, adding just the ...props.navigation.state.params line is no more work than calling the mapping function it in the router setup. |
Currently props may be passed to a scene as in: imho there is a good argument that
What is your opinion @satya164 ? thanks a lot!! |
While this is true upto an extent, this is largely a non issue when you use destructuring.
It should. The navigation action is like a URL on the web. The URL contains all the information to bootstrap the screen, similarly the navigation action contains all information to bootstrap the screen. This architecture makes it very easy to implement things like deep linking and keeps the screens deterministic so that the same navigation action will not produce two entirely different screens. If you're confused how you'd pass props around screens, imagine how you'd do with web pages and URLs. It's the same. |
so there we go @wvteijlingen. I still feel that having the tight coupling is wrong. Many people will follow the docs and rewrite their components and change |
Sure, your component can rely on other stuff rather than props, similar to how a screen would rely on other stuff than
Avoiding extra typing shouldn't be the argument for a wrong architecture.
Since you're using a beta version, APIs can change, so I'm not sure what you mean by this. Surely there are a lot of other APIs in React Navigation than
Sure, that is totally fine, it doesn't change the contract. |
Thanks for the explanation @satya164 👍 |
@satya164 I disagree with your assertation that:
Having to jump through these hoops to pass props shows that props are not treated as first-class values in this library which is really troubling. It feels wrong to have to access props.navigation.state on a component that does not need to navigation. I'm with @wvteijlingen that coupling props to navigation is the wrong move in the long run. In fact it's already hitting right now because all of the examples of passing props in this thread go against the docs because you do not recommend passing non-string types through params: #952. |
Which part? The extra typing? Yes, extra typing is a bit of hassle, but that's the not a strong argument for the wrong architecture. And extra typing can be avoided when you do something like @vonovak did, just use an HOC.
Why do you think that? You get
Have you made a website before? How do you know what to render from a url like While navigation on native can be a bit more work due to the animations, the basic principle stays the same as the web. Dispatching a
We don't recommend it. But it's possible, you can still do it and I don't think we have plans to restrict it if it's not a string. Probably we should revisit this later and see how we can support any serializable value. Consider the scenario where you didn't have |
I should have stated my position better. I meant that extra typing shows that certain habits/actions are promoted by the library. So we can't simply dismiss something because it's doable but takes extra typing. We should promote good habits/actions by reducing the amount of typing required for them. And even though you can pass props through a HOC, I don't think it should be a requirement as it is now because we want to promote passing props.
I have, and frankly I find ad hominem statements like this about my experience distracting from the discussion at hand. I'm not arguing just having params or just having props, I'm merely stating that the current solution of just params does not seem to place an emphasis on props. So if we want props we need to bundle them to the navigation object which is coupling the data to the navigation. I just think there should be a separate way promoted by React Navigation to pass props when navigating. |
Reducing amount of typing doesn't necessarily mean promoting good habits. I acknowledged it's a minor annoyance. But there's no better API at the moment.
Sorry, but how does asking if you have made a website is making a statement? React Native has people from all kinds of fields and it's fair to assume one might not have any experience with websites. I clearly described how navigation state is related to URLs and you need to have experience with the web to understand that.
Again, just having something inside an object doesn't make it not a prop. I don't agree with the statement that it reduces emphasis on props.
I think I have described in detail why the coupling is good and it's advantages. And how navigation state resembles a URL. Note that URLs don't contain usually data, they contain information on how to fetch data. Anyway, just saying that it should be done in a different way doesn't help. You need to consider all the reasons it is the way it is, and suggest an API that still satisfies the reasons while being easier. ✌ |
@satya164 I think you have a valid point about URL style params.
I think there will be lots of code to convert this to redux style actions and reducers |
Thanks for the discussion guys! I've been reading through all the opinions. So, I don't mind the extra typing. Sure, it may not look as 'nice' and take some extra seconds to type, but I don't think those are good arguments for deciding on an API. For the coupling though I still feel it could be improved. I actively develop for both web and iOS, so I'm aware of the overlap, differences and routing architectures. What I would like is the ability to use my components without having to fit them into a certain pattern that is defined by the navigation. The way I see it, the props of a component are pretty much the public API of that component. What that API looks like can be defined by the component itself. Whether the props came from navigation, or just "plain props" shouldn't matter to the component. The navigation layer then sits on top of these components, and merely orchestrates displaying and transitioning. The current situation with react-native not just orchestrates the transitions, but also defines a large portion of the public API for the components (the props), which I think is too rigid. If you look at the iOS navigational architecture, the UIViewControllers are (mostly) decoupled from navigation. The parent controller takes care of preparing any presented controllers before the transition happens. This is very flexible, because what happens in this preparation phase is entirely up to the controllers instead of the navigation. |
@xiaohanzhang For example, what will you recommended for passing non-string callback between Screens? I'd not use callbacks between screens. I'd use a global event emitter or redux. @wvteijlingen Sure, It's just JavaScript. Use the language to do achieve what you want. It might make sense to splat the params onto the component, but it has a chance to conflict with other props the navigator might want to pass to screens, and probably very easy to deviate from this behaviour for custom navigators. You're free to use an HOC to do that. you don't have to do |
@satya164 to me, passing callbacks between screens also sounds a little strange. But still, I'm interested in knowing what the limitations of passing more complex (non-serializable) objects to screens are. It would be great if the docs explained this. What makes the serialization possible and thus what will I not have without the serialization? Thanks a lot! |
The library is too opinionated here. The user should have the ability to decide how data is passed between components. In particular, the additional data I'm passing is defined in user-land. Stuff inside |
Hmm, honestly it's even worse. What if you guys decide you need to change the structure of the navigation props? |
i have a very similar thing on my navigator.
|
@vonovak Nice work! Haven't tried it yet but being able to just use static navigationOptions = (props) => ({
title: props.name,
headerRight: (
<HeaderButton title="Sort" onPress={() => props.navigation.navigate('DrawerOpen')} />
),
}); |
@vonovak It only works if it is an React component. If is a StackNavigator, it gets: |
@tom29 that's because it only is intended for screen components |
@vonovak so, there's no way to pass props to a screen is a StackNavigator? |
@tom29 there's no such thing as a StackNavigator screen. StackNavigator is a component that has the screens (user-defined components) in it. When using react-navigation, you will define the screens and put them in a StackNavigator. You won't program the StackNavigator itself, but you'll program what the screens look like, which is where you use this library. |
I mean if i do as your way
It works, then if
what's FirstScreenComponent now? Its not StackNavigator screen? Doesn't matter what it called. |
@duhseekoh I'm not sure why your answer got down votes. Splitting into I think it is a rare case where Anyone please correct me if I am wrong or anything wrong with what @duhseekoh said. |
@srinivasdamam, I tried to do exactly that, but I ran into a couple of problems. Basically what happened was that my 'semi-dumb' component did some data loading and used that data to generate the title for the navigation header. Unfortunately this component had no access to the navigation, so could not set the header. You could solve this by creating a callback for this, or by lifting the data loading up into the smart component. If you do the latter, that means that now you have a smart component that combines data loading and navigational concerns. Then if you want to use that data loading logic in multiple places in your nav structure, you either have to duplicate it or split it into yet another component. Anyway, when I pursued this I ended up with three levels: YMMV ofc. That said: I don't think the team for this lib is willing to change this behaviour, so I'm probably beating a dead horse here. But in this entire thread I've only read workarounds and other "solutions", I cannot find one single reason why this design was chosen and why it has to be so convoluted. |
So, now that this project is seemingly getting more attention again, I would like to know if passing regular props will ever be implemented. I would like to know what the official stance is currently. I've been bashing my head against the wall trying different workarounds and other 'solutions' but it doesn't do it for me. Sadly, neither is the current implementation itself. |
@reinvanimschoot please check the 6th post, there is a link to a package that will allow to use props directly. |
@vonovak I tried the package but it didn't work. I had a top level |
For anyone considering making the switch to Wix, I did it yesterday and I wish I'd done it a long time ago. It has a simple, powerful API that works the way you think it should and avoids a lot of these issues. |
@johngoren did you use v1 or v2? |
This is suuuuuuuuch an awful design decision... |
@pvinis v1. After React-Navigation, it's like a dream. Much closer to the way navigation works on, for example, iOS and it has never made me feel like I'm going insane the way React-Navigation does. |
oh god, this is just disgusting |
More than half a year has passed and still not a word from the official maintainers on this terrible design decision. |
A tip:
If you have a custom header which connect to redux and need the screen level params, try to get those from the current
instead of the
|
@johngoren Thanks for the heads up on Wix. Was debating between the two but thought this was supposed to be nicer. Will reinvestigate Wix. Not subtly bashing the maintainers here. Just genuinely thankful for the suggestion. Didn't read the entire thread but would just like to remind people they can make this a bit less ugly by destructuring. In this example <Button onPress={() => this.props.navigation.navigate(`GiftDetails`, { gift })}> export default class GiftDetails extends React.Component {
constructor (props) {
super(props)
this.state = {
gift: this.props.navigation.state.params.gift,
}
const { title, price } = this.props.navigation.state.params.gift
this.gift = { title, price } // someone let me know if there's a fancier destructure to avoid this duplication
}
render () {
return (
<View>
<Text>{this.gift.title}</Text>
<Text>{this.gift.price}</Text>
</View>
)
}
} |
For all those lost souls who will eventually get here by Googling their brains out. The easiest way to do this is to create a "higher order component" into which you pass the prop(s) which renders the original component with it, and it goes something like this:
Hope this helps... |
I'm only here to unsubscribe from this thread but the solution proposed there will create a new class (aka function) object literally every time that render function is called. It works but is pretty suboptimal and will cause a lot of churn in the underlying JS engine. |
Why not just do this on every screen component, or some variation of it:
|
@allthetime lol, get out of here with your super simple/nice solution! We're complain' here!!! >: ( |
@allthetime Yeah that's exactly what I had in mind ^^ I'm gonna try that before switching to redux |
I have genuinely cited this thread in several react native interviews as an example of how tentative and opinionated navigation still is. |
@allthetime, this won't work if you use PropTypes for typechecking (the proptypes are checked before your code is executed). |
I see that it is currently possible to pass data to a new screen using the second parameter of the
navigate
function:navigate('Chat', { user: 'Lucy }
. These then end up inthis.props.navigation.state.params
on the presented component.Is it also possible to set regular props on the presented component? So that I can just access them as
this.props.user
instead ofthis.props.navigation.state.params.user
?If that is not possible, then all my components are going to be tightly coupled to the navigation, and can never be displayed in any other way. That would be a huge downside to using react-navigation imho!
I hope I'm missing something here. Could someone enlighten me? :)
The text was updated successfully, but these errors were encountered: