I don't want to implement Flux/Redux, so I need to pass parameters through the Router. I'm trying this approach (in a nutshell):
import React, { Component, Navigator, StyleSheet, View, Text } from 'react-native'
import { Router, Route, Schema, Actions } from 'react-native-router-flux'
import Button from 'react-native-button'
export default class AppRouter extends Component {
constructor(props) {
super(props)
this.state = { items: [0] }
}
render() {
console.log('Item count on Router render:', this.state.items.length);
return (
<Router hideNavBar={false}>
<Schema name="default" sceneConfig={Navigator.SceneConfigs.FloatFromRight}/>
<Route name="main" schema="default" initial={true} component={Main}
title="Main" rightTitle="Search" onRight={() => Actions.search()}
items={this.state.items} />
<Route name="search" schema="default" component={SearchView}
onResult={(result) => this.setState(result)} />
</Router>
)
}
}
class Main extends Component {
constructor(props) {
super(props)
this.state = { items: this.props.items ||聽[] }
}
componentWillReceiveProps(props) {
console.log('Item count on componentWillReceiveProps:', props.items.length);
this.setState({ items: props.items })
}
render() {
console.log('Item count on Main render:', this.state.items.length);
return (
<View style={{ marginTop: 64 }}>
{this.state.items.map((item, i) => <Text key={i}>{item}</Text>)}
</View>
)
}
}
class SearchView extends Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={this.callback.bind(this)}>Close</Button>
</View>
)
}
callback() {
Actions.pop()
this.props.onResult({ items: [1, 2, 3] })
}
}
It prints:
// App load
Item count on Router render: 1
Item count on Main render: 1
// Press 'Search' button
Item count on componentWillReceiveProps: 1
Item count on Main render: 1
// Press close button
Item count on Router render: 3
Item count on componentWillReceiveProps: 1 // did not pass items
Item count on Main render: 1
Item count on componentWillReceiveProps: 1
Item count on Main render: 1
Am I doing something wrong?

+1
Seems that routes props are stored internally in the constructor and they are never updated again.
Something like this is happening:
// this.state.items === 1
<Route items={ this.state.items } />
// this.props.items inside the route handler will be always 1
// It will never change even if the state of the parent changes
Please, fix this. It's a big stopper from using this library.
@mrpatiwi It is NOT intended usage of this component (passing 'items'), i don't see any mention of this in documentation. You could propose PR so i could review and apply if it doesn't break anything.
How can I communicate navigation items (left and right buttons) to the component?
In my case I am using freezer as the state holder. I would need to pass the current app state down to the current screen. In the docs is said that I can pass any prop to the route:
<Route appState={ freezer.get() }/>
But the first value is cached so the app is not reacting to the state changes. Props are not passed down as expected, they work like setting an initial value.
@mrpatiwi Maybe i don't understand the problem. When you call Actions.ROUTE_NAME(params), these params passed to 'component' prop as well to onLeft and onRight props (functions for navigator).
Closing it for now, because don't understand what the problem is...
I'm having the same problem. In my root App.js where I have my Router and Routes, I listen for changes to my state holder (also using freezer.js like @arqex). It looks something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = freezer.get();
}
componentDidMount() {
var self = this;
freezer.on('update', function(newValue) {
self.setState(newValue);
});
}
render() {
return (
<Router {...this.state}>
<Route name="login" component={Login} title="Login" initial={true}/>
<Route name="teamPicker" component={TeamPicker} title="Team picker"/>
<Route name="mainPlay" component={MainPlay} title="Main Play"/>
</Router>
);
}
}
Since ...this.state is now available as properties in each of the Routes, I would expect them to update whenever the freezer triggers an update to the App.js state
For anyone else who comes upon this issue, it seems it's been fixed in master but not yet pushed to npm. Cloning from master and requiring locally, prop changes to the Router now propagate to its Routes
Thanks for the update. Glad I'm not the only person who got stuck on this.
I feel like I'm having the same problem. I'm calling a scene mainEntry and it's calling it, but the object I'm sending through isn't available on the props.
Actions.mainEntry({view: 'SignUp'});
In my mainEntry component the passed data doesn't show up at all. Am I just being a dum dum?
@keeleycarrigan I'm having the same issue, did you find a way around it?
@ospfranco In general, the problems I've had with this module I have solved by adding some piece of information to the apps overall state. In this instance, I created a view component that's just a shell for another view to be injected into. So after the launch screen the initial scene is this shell and then I set a value on state that the shell uses to load the proper component. I couldn't get the back button on the title bar to work properly either. So I just made my own title bar that I control with a "navigation" key on the app state. Even for all it's problems, this module is still the most straightforward to implement for navigation.
Let me know if that makes sense.
maybe it could help:
for example if you started a Scene, then you can use something like this
Actions.pop({refresh: {prop: "value"}});
as stated here
allows to refresh the props of the scene that it pops back to
@keeleycarrigan I was experiencing the same problem as you.
The problem was that I was trying to pass props from Scenes belonging to the same parent Scene. I solved by moving this scene to its own parent. Picking up on your naming, what I had was:
<Scene initial='true' key='main'>
<Scene key='anotherEntry'>
<Scene key='mainEntry'></Scene>
</Scene>
</Scene>
By the above, you can see that mainEntry is a child of anotherEntry.
And I solved by doing:
<Scene initial='true' key='main'>
<Scene key='anotherEntry'>
</Scene>
<Scene key='mainEntry'></Scene>
</Scene>
And now the mainEntry is not a child anymore. By doing that I was able to now pass the props.
Not sure if this is your case, but I hope it helps.
Thanks @pmadruga. I actually just ended up rebuilding my app using NavigationExperimental directly. Too many issues with this module.
If you send some value using actions and put a breakpoint in your scene before it renders, you can easily find the the object that you are passing in that scene. So here is how its done:
Pass the object to the scene like this
Actions.mediaViewScene(message);
Now in your scene, you can access this object in the data variable.
class MediaViewScene extends Component {
render() {
this.props.data <---- this contains the object that I passed.
Yeah, it doesn't seem to be working for me.
I'm using a Drawer (from react-native-drawer) with multiple tabs within my drawer scene. When using Actions.ROUTE(_PROPS), the scene only has navigation state in its props
@gablorquet : i'm in the same situation. Didi you solved?
No, unfortunately. I switched to React Navigation but it has pretty much the same problem. As our app is in very early stages of development, I've settled on waiting for a fix.
@gablorquet : probably I find a solution.It was as simple as creating the props at router level
This is my Scene definition,
const scenes = Actions.create(
<Scene key="root" >
<Scene key="mainBucket" component={NavigationDrawer} >
<Scene key="mainBucketInternal" >
<Scene
....
onRight={(scene) => scene.openCreateGroup()}
Note openCreateGroup: here openCreateGroup is s a property I created at Router level.
In my specific case, I'm passing a redux action to Router, I use the connect() function
import { connect } from 'react-redux';
...
import {
openCreateGroup
} from './actions';
....
(scenes definitions like above)
...
class AppRouter extends Component {
render() {
return (
<Router
scenes={scenes}
{...this.props}
/>
);
}
}
const mapStateToProps = ({ drawer }) => {
const { open } = drawer;
return {
open
};
};
const mapDispatchToProps = {
openCreateGroup
};
export default connect(mapStateToProps, mapDispatchToProps)(AppRouter);
If you add a console.log(this.props)into render() method, you'll see both open and openCreateGroup. The first is a piece of state (used to integrate Drawer) and the second is an action.
If you need that your Scene properties are passed down to Scene children, use the passProps property into the specific scene definition.
Of course, it works, but it's not so clean
Finally I got a perfect solution about this. It is lazy load, a simple factory method you know!
@denghejun please add more details
@realtebo
Ok, you can pass a factory method as a prop into your screen, the method return a reference type value, here is this.props, see below:
<Scene key='home' getNewerProps={()=>this.props} {....other props} />
And then in your scene view, use the method like this:
const newerProps = this.props.getNewerProps(); // newerProps must be latest.
Actions.Refresh({ title: newerProps.title }); // then refresh current screen's title. it works.
Two Points:
So we can get the newer props value from the same reference memory address. So the most important thing is keep the reference, you can get it whenever the value changed.
Most helpful comment
I feel like I'm having the same problem. I'm calling a scene
mainEntryand it's calling it, but the object I'm sending through isn't available on the props.Actions.mainEntry({view: 'SignUp'});In my
mainEntrycomponent the passed data doesn't show up at all. Am I just being a dum dum?