react-navigation: access stateChange event of nested navigator - react-native

I'm trying to add a state change event to a nested navigator.
Usually I would define it like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: NextNavigator,
},
})
but I need something like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props)=>{ return <NextNavigator {...props}/> },
},
})
where the screen object is a function.
The problem is I have no idea what signature screen as a function has, and how to pass it to the navigator
I'm getting close with something like this:
screen: (props, state)=>{ return <NextNavigator {...props} state /> },
But it's still not correct.
Does anyone know how this works?

If you don't want #JeffGuKang to answer, you can declare and use global variables and functions.
let NICKNAME = '';
function setNickName(data) {
NICKNAME = data;
}
function getNickName() {
return NICKNAME;
}
export { setNickName,getNickName }
...
//set data
import { setNickName} from './globalpath';
setNickName(data)
....
//get data
import { getNickName} from './globalpath';
getNickName()

Use Params
Use setParams, getParam for changing state in components wrapped by navigatior.
A Component (Set changing value)
const name = this.props.navigation.setParams({ name: this.state.name, age: this.state.age });
B Component (Get value)
const name = this.props.navigation.getParam('name', 'DefaultValue');
const age = this.props.navigation.getParam('age' 0);
setParams is a trigger to run getParam in other components is wrapped navigation.
Extend Navigator
You can extend the component created by Naivgator as below. link
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = MyStack.router;
state = {
a: 'b',
}
render() {
const { navigation } = this.props;
return <MyStack {...this.state, ...this.props} />;
}
}
Or it is able to use functional component instead.
const MyStack = createStackNavigator({ ... });
CustomNavigator = (props) => {
return <MyStack {...props} />
}
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props) => { return <MyStack {...props} /> },
},
Third: CustomNavigator,
})
And do not forget import `React` to use functional component.

Related

How do I create an embedded Stack navigator inside a React Native formSheet modal?

Like so:
I'm running react-navigation v4.
First you have to follow the tutorial on how to set up a react-navigation modal, the one that has a jump animation and doesn't look like the native formSheet. You have to set up a stack navigator with your root navigator as one child and the modal as another:
And it scales, because you can have more than one of these modals as children.
The code for this is the following:
const RootNavigator = createStackNavigator(
{
index: { screen: AppNavigator },
[NC.SCREEN_ROOM_INFO]: { screen: RoomInfoNavigator },
[NC.SCREEN_CHAT_CREATE]: { screen: RoomCreateNavigator },
[NC.SCREEN_CHAT_SEARCH]: { screen: ChatSearchNavigator },
[NC.SCREEN_CHAT_GIF_PICKER]: { screen: GifPickerNavigator }
},
{
mode: 'modal',
headerMode: 'none',
transitionConfig: () => ({
transitionSpec: {
duration: 0
}
}),
transparentCard: true
}
)
Then you need to implement these, from my example, 4 navigators that will be displayed as modals each like so:
// Here you'll specify the screens you'll navigate in this modal, starting from index.
const RoomInfoStack = createStackNavigator({
index: { screen: NavigableRoomInfo },
[NC.SCREEN_ROOM_ROSTER]: { screen: NavigableRoomRoster },
[NC.SCREEN_ROOM_NOTIFICATION_PREFERENCES]: { screen: NavigableRoomNotificationPreferences },
[NC.SCREEN_ROOM_EDIT]: { screen: NavigableRoomEdit }
})
type NavigationComponent<T = any> = {
navigation?: NavigationScreenProp<NavigationState, T>
}
type Props = NavigationComponent
// THIS code is from the react-navigation tutorial on how to make a react-navigation modal:
// https://reactnavigation.org/docs/4.x/custom-navigators/#extending-navigators
class RoomInfoNavigator extends React.Component<Props> {
static router = {
...RoomInfoStack.router,
getStateForAction: (action, lastState) => {
// check for custom actions and return a different navigation state.
return RoomInfoStack.router.getStateForAction(action, lastState)
}
}
constructor(props) {
super(props)
this.onClose = this.onClose.bind(this)
}
onClose() {
this.props.navigation?.goBack()
}
// And here is the trick, you'll render an always open RN formSheet
// and link its dismiss callbacks to the goBack action in react-navigation
// and render your stack as its children, redirecting the navigator var to it in props.
render() {
return (
<Modal
visible={true}
animationType={'slide'}
supportedOrientations={['portrait', 'landscape']}
presentationStyle={'formSheet'}
onRequestClose={() => this.onClose()}
onDismiss={() => this.onClose()}
>
<RoomInfoStack {...this.props} />
</Modal>
)
}
}
export { RoomInfoNavigator }
This export is what our root stack imported before. Then you just need to render the screens, I have a pattern that I do to extract the navigation params to props in case this screen is ever displayed without navigation:
const NavigableRoomInfo = (props: NavigationComponent<RoomInfoProps>) => {
const roomInfo = props.navigation!.state!.params!.roomInfo
const roomInfoFromStore = useRoomFromStore(roomInfo.id)
// Here I update the navigation params so the navigation bar also updates.
useEffect(() => {
props.navigation?.setParams({
roomInfo: roomInfoFromStore
})
}, [roomInfoFromStore])
// You can even specify a different Status bar color in case it's seen through modal view:
return (
<>
<StatusBar barStyle="default" />
<RoomInfo roomInfo={roomInfoFromStore} navigation={props.navigation} />
</>
)
}
Sources:
https://reactnavigation.org/docs/4.x/modal
https://reactnavigation.org/docs/4.x/custom-navigators/#extending-navigators

How to pass props to react navigation in react native app

I am using HOC to get current user details on login for each route in my react native application using ApolloClient.
Below is my main component App.tsx:
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createStackNavigator } from 'react-navigation-stack';
const client = new ApolloClient({
cache,
link: errorLink.concat(authLink.concat(httpLink))
});
const TabStack = createBottomTabNavigator({
Feed: {
screen: Feed
},
Upload: {
screen: Upload
},
Profile: {
screen: Profile
}
})
const MainStack = createStackNavigator(
{
Home: { screen: TabStack },
User: { screen: UserProfile },
Comments: { screen: Comments },
Post: { screen: Post }
},
{
initialRouteName: 'Home',
mode: 'modal',
headerMode: 'none'
}
)
const AppContainer = createAppContainer(MainStack);
//Here I assign HOC to AppContainer
const RootWithSession = withSession(AppContainer);
class App extends PureComponent {
constructor(props) {
super(props);
}
render() {
return (<ApolloProvider client={client}>
<RootWithSession/>
</ApolloProvider>)
}
}
export default App;
Below is HOC withSession.tsx:
export const withSession = Component=>props=>(
<Query query={GET_CURRENT_USER}>
{({data,loading,refetch})=>{
if (loading) {
return null;
}
console.log(data);
return <Component {...props} refetch={refetch}/>;
}}
</Query>
)
I want to pass refetch function to all the components
<Component {...props} refetch={refetch}/>
How to do this? Any help is greatly appreciated.
I was able to pass refetch function to all Components from HOC withSession component using ScreenProps of StackNavigator as below:
export const withSession = Component=>props=>(
<Query query={GET_CURRENT_USER}>
{({data,loading,refetch})=>{
if (loading) {
return null;
}
if(props && props.navigation){
//props.refetch = refetch;
console.log(props)
//props.navigation.refetch = refetch;
props.navigation.setParams({refetch});
}
return <Component screenProps={{refetch}} {...props} refetch={refetch}/>;
}}
</Query>
)
Now, I am able to get refetch method using this.props.screenProps.refetch

How to pass a string to screens into a BottomTabNavigator

I have a BottomTabNavigator with 4 sreens inside of it, I would like to pass a string into some of them.
How can I do that ? Thank you.
class Main extends Component {
static navigationOptions = {
title: 'Developpe Inclinné',
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#e8142d',
},
};
render() {
const myPath='aRouteForFirebase'
return (
<AppContainer myPath={myPath} />
);
}
}
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session, // needs the props
Resultats // // needs the props
})
const AppContainer = createAppContainer(TabNavigator);
Here is a better implementation:
class Main extends Component {
constructor(props) {
super(props)
this.state = {
myPath: 'aRouteForFirebase',
}
this.AppContainer = createAppContainer(this.TabNavigator)
}
TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: () => <Session myPath={this.state.myPath} />},
Resultats { screen: () => < Resultats myPath={this.state.myPath} />}
})
render() {
const { AppContainer } = this
return (
<AppContainer />
)
}
}
Just for clarification:
class Main extends Component {
static navigationOptions = {
title: 'Developpe Inclinné',
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#e8142d',
},
};
render() {
const myPath='aRouteForFirebase'
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: () => <Session myPath={myPath} />},
Resultats { screen: () => < Resultats myPath={myPath} />}
}
const AppContainer = createAppContainer(TabNavigator);
return (
<AppContainer />
);
}
}
However I don't think this is good looking code, maybe you should think about integrating Redux
I can't find anything in react-navigation documentation which supports this. But I may be wrong. I can suggest one other way of doing it. Create a HOC and wrap your components with it. They all will have access to the common string.
const SomethingCommonHOC = (Component, extraProps ) => {
return class SomeComponent extends React.Component {
render() {
return <Component {...this.props} {...extraProps}/>;
}
};
};
// Use it something like this.
SomethingCommonHOC(Session);
Thanks
Edit: I understand it is difficult to explain actually. That's the reason i am not big fan of HOCs. :). I will give more try to explain you the edit required.
Create a new file:
Put this component definition in it:
const CommonHOC = (Component, extraProps) => {
return class SomeComponent extends React.Component {
render() {
return <Component {...this.props} {...extraProps} />;
}
};
};
export default CommonHOC
Then import it in your component files , in your case DeveloppeInclinne, Chrono, Session, Resultats.
In component where your need to pass common string: Let's say you are editing in session component file
import CommonHOC from the "path to CommonHOC"
export default CommonHOC(Session, {
myString: "myString"
})
Note: you can string like constant dynamic by converting 2nd param a the object and spreading inside the common component
You can do this:
const TabNavigator = createBottomTabNavigator({
DeveloppeInclinne,
Chrono,
Session: { screen: props => <Session {...props} />},
Resultats { screen: props => < Resultats {...props} />}
}
// in class Session ...
console.log(this.props.myPath)
=> aRouteForFirebase

react native pass value from drawer navigator

In App.js
const MyApp = createDrawerNavigator({
Home: {
screen: Home
},
Form: {
screen: Form // how to pass value Id = 0
},
{
....
}
})
in Form.js
componentDidMount() {
if (this.props.navigation.getParam('Id', 1) !== 0) {
...
}
else {
...
}
}
Can anyone tell me how to achieve this?
Thanks in advance
Use Screen Props:
const MyApp = createDrawerNavigator({
Home: {
screen: Home
},
Form: {
screen: props => <Form {...props} id={0} />
},
{
....
}
})
In <Form {..props} id={0} /> you need to pass all props using {...props} so that navigation work correctly.
Then, you can access props from the Form screen and use it according to your use case.

React native navigation class function in header

I've got a problem with react native navigation and nested navigators.
Basically, the nested navigators (tab in a page) work pretty well. But when i add a button in the header with the _saveDetails function, it throw me an undefined function if i'm in the Players tab, and it works well when i'm on the Teams tab
Does anyone have an idea of what am i doing wrong? Thanks.
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerRight: <Button title="Save" onPress={() =>
params.handleSave()} />
};
};
_saveDetails() {
console.log('clicked save');
}
componentDidMount() {
this.props.navigation.setParams({ handleSave: this._saveDetails });
}
render() {
return (
<View />
);
}
}
const MainScreenNavigator = TabNavigator({
Players: { screen: HomeScreen},
Teams: { screen: HomeScreen},
});
const SimpleApp = StackNavigator({
Home: { screen: MainScreenNavigator },
Player: { screen: PlayerPage },
});
Please try this one and let me know if you fetch any other problem. Just convert your code according to below code.
static navigationOptions = {
header: (navigation, header) => ({
...header,
right: (
navigation.navigate('Settings')}>
Settings
)
})
}
Thanks
Just change your code become like this
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
// const { params = {} } = navigation.state; //delete this
return {
headerRight: <Button title="Save" onPress={() =>
navigation.state.params.handleSave()} /> // add navigation.state
};
};
.....