Way to dynamically set a React Navigation screen title using MobX store value? - react-native

On my default React Navigation tab screen I'd like to set the screen's title to a value from a MobX store. It's my understanding that the only way to do this is to pass the value via a param--so I can't just put the MobX value in the 'title: ' field... but as this is the 'default' screen I'm not passing it anything.
Default screen:
export default class HomeScreen extends Component {
static navigationOptions = ({ navigation, screenProps }) => ({
title: `This is ${navigation.state.params.title}`,
I've attempted to make use of setParams during componentWillMount, but console.log shows me it must be happening too late, so I get an empty object in the title.
Any idea how to do this?

Had the same problem today and figured it out.
Simply change your navigationOptions to a function instead of an object, that way it will re-evaluate on state changes. Here's the fix for you, as described by the manual at https://reactnavigation.org/docs/intro/headers#Header-interaction-with-screen-component:
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
const title = `This is ${params.title}`;
return { title };
};

Related

It is possible to return to previous screen with data on react native?

Let's say our current stack is ScreenA -> ScreenB. How can I return to ScreenA with a result (data) from ScreenB? Similar to android startActivityForResult and onActivityResult
Edit: The goal is to return to ScreenA from ScreenB with a result, keeping ScreenA state if possible.
you can just navigate to ScreenA with parameters.
In your ScreenB, add the following to navigate:
const ScreenB = ({navigation}) =>{
...
navigation.navigate("ScreenA",{data});
...
In your ScreenA, you listen for it.
const ScreenA = ({navigation, route}) => {
useEffect(()=>{
//this block listens for data change, then act on it.
},[route.data]);
...
}
Please refer to this LINK.
Screen 1:
onPress=()=>{
this.props.navigation.navigate('Screen2', {
yourArray: this.state.yourArray, });
}
}
Screen 2:
constructor(props) {
super(props);
var params = props.navigation.state.params.yourArray;
}
In this example, we will input the value in the first screen and get it into the second screen.
The value (param) is passed as an object in the first screen to the navigation.navigate function as:
this.props.navigation.navigate('RouteName', { /* params go here */ })
The same value (param) is read in the second screen as:
this.props.navigation.getParam(paramName, defaultValue)

Deep linking into an already open screen with different params. Is it possible?

I'm working on an app which has a category screen where the relevant posts of the category are displayed. I'm using react-navigation to navigate between the screens and handle the deep linking. The category screen can be accessed in-app or via deep link. To access the category screen via deep link I'm using something like myapp://category/:id.
If the app is already opened and is focused on the category screen the deep link does nothing.
I've currently "fixed" it using the componentDidUpdate life cycle method to compare the ID stored in the state and the ID in navigation.getParam.
componentDidUpdate = () => {
const { navigation } = this.props;
const { categoryID } = this.state;
const paramID = navigation.getParam('id', null);
if (paramID !== categoryID) {
this.setState({ categoryID: paramID }, () => {
// fetch data
});
}
}
However, this seems like a dirty fix to me. Is there a better way of doing this? Maybe opening all deep links using push instead of navigate via react-navigation?
For anyone who got here wondering how this can be done, like myself, here's the solution:
You can use the getId Stack.Screen property. You just need to return the unique ID from the getId property. Here's an example:
Let's say we have a UserScreen component, which is part of our Stack navigator. The UserScreen has one route param: userId. We can use the getId property like this:
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
const AppStack = () => {
const getUserRouteId = ({ params }) => {
return params && params.userId;
};
return (
<Stack.Navigator>
{/* Other routes */}
<Stack.Screen name="User" component={UserScreen} getId={getUserRouteId} />
</Stack.Navigator>;
);
};
Now when you try to normally navigate to this route, using the navigate function, if the param userId in the navigation action is different than on the UserScreen which you're currently on, it will navigate you to a new user screen. React Navigation deep linking uses the navigate function internally.

How to set navigation params properly

I use react-navigation for navigation in my app. I have some modal view (SomeComponent) and I wanna set custom title during presenting animation. The problem is that title is changing but after presenting animation is finished.
I've tried set params in other component lifecycle callback but it didn't work.
I don't want to set this parameter with navigate function because I don't have full data to set it.
class SomeComponent extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam("customTitle"),
}
}
componentDidMount() {
this.props.navigation.setParams({ customTitle: "Some custom title" })
}
}
I wanna set title before animation is finished.
Try to set the params in the getDerivedStateFromProps method as this is the method that is fired after the constructor and before any render methods

Passing props to a component to use in testing a SnapShot

Screen-test.js
it('renders the Engagement Detail modal', () => {
const tree = renderer.create(<EngagementDetailModal details={engagementData}/>).toJSON();
expect(tree).toMatchSnapshot();
})
the component I am trying to test
class CompanyDetailView extends React.Component {
render() {
const data = this.props.navigation.getParam('details');
return (
// rest of the code
)
}
}
My data variable is just a bunch of static data. Using jest I am getting this error TypeError: Cannot read property 'getParam' of undefined and it is pointing to this line const data = this.props.navigation.getParam('details');
I know the component works as i run the app, just the test is failing.
I thought that by just providing the prop to the component in my Screen-test.js file it would work the same but i get undefined. How can i test this?
Also I am passing the prop using react navigation like this onPress={() => this.props.navigation.navigate('EngagementDetailModal', {details: this.props.data})
It cannot read the property getParamof navigation because you haven't mocked the navigation. So when the test case runs, it doesn't know what this.props.navigation does.
Add the following after imports section in your Screen-test.js .
const testProps = props => ({
navigation: {navigate: jest.fn(), getParam : jest.fn()},
...props,
})
Hope this helps!!

How to navigate in mobx store using react navigation?

I can use this.props.navigation from screen component to navigate. How should I do the similar in mobx store file? Or should I perform navigation in store?
I read the Navigating without the navigation prop article, but it seems only works for screen components, right?
Someone says use global variable to store a this.props.navigation reference and use it anywhere, but I don't like the idea...
Yes either:
forward the navigation class to the store when calling the method:
// add nivagation parameter to store fucntion:
this.props.store.handleSomething(data, this.props.navigation);
Or you can singleton the navigator (warning only works for one ofc):
return <Navigator ref={(nav) => this.props.store.navigator = nav} />;
after this is rendered it will set the navigator property in the store.
But I would suggest to store all your routing state also in a routing store like this: https://github.com/alisd23/mobx-react-router.
This way you always have easy access to the navigation state and you can be sure everything properly re-renders. (when in render function in components also depends on navigation changes!)
You can keep all your states including navigation state in mobx store.
For example:
// sourced and modified from https://github.com/react-community/react-navigation/issues/34#issuecomment-281651328
class NavigationStore {
#observable headerTitle = "Index"
#observable.ref navigationState = {
index: 0,
routes: [
{ key: "Index", routeName: "Index" },
],
}
// NOTE: the second param, is to avoid stacking and reset the nav state
#action dispatch = (action, stackNavState = true) => {
const previousNavState = stackNavState ? this.navigationState : null;
return this.navigationState = AppNavigator
.router
.getStateForAction(action, previousNavState);
}
}
// NOTE: the top level component must be a reactive component
#observer
class App extends React.Component {
constructor(props, context) {
super(props, context)
// initialize the navigation store
this.store = new NavigationStore()
}
render() {
// patch over the navigation property with the new dispatch and mobx observed state
return (
<AppNavigator navigation={addNavigationHelpers({
dispatch: this.store.dispatch,
state: this.store.navigationState,
addListener: () => { /* left blank */ }
})}/>
)
}
};
Then you can directly call the dispatch action of the store to navigate to a new screen.
Send this one this.props.navigation as a parameter to the store. Then use as you use on the component side.
LoginStore.login(this.props.navigation)
in the LoginStore
#action login = (navigation) => { navigation.navigate('Page');}