React Navigation Warning: "jumpToIndex is not a Function" - react-native

I recently updated react-navigation to version 2.18.0 and a section of my code which used to work no longer does. After combing through the documentation, I'm still having trouble reproducing the functionality I had before.
Essentially I wanted all the data that the stats screen needed to be loaded before jumpToIndex is called, so that the StatsScreen Component had access to updated data before render().
This functionality used to work, but now I'm getting an "Unhandled Promise Rejection: TypeError: jumpToIndex is not a function." warning. and jumpToIndex never happened.
In App.js I changed TabNavigator to createBottomTabNavigator, and made the necessary changes for the update.
const RootNavigator = createBottomTabNavigator({
Home: {
screen: HomeStackNavigator,
navigationOptions: ({ navigation }) => ({
//Navigation options here
),
}),
},
StatsScreen: {
screen: StatsScreen,
},
}, {
lazy: false,
});
In StatsScreen.js:
export default class StatsScreen extends Component {
static navigationOptions = ({ navigation }) => ({
tabBarOnPress: async (tab, jumpToIndex) => {
if (!tab.focused) {
await navigation.state.params.update();
jumpToIndex(tab.index);
}
},
});
async componentDidMount() {
this.props.navigation.setParams({
update: this._updateStats.bind(this),
});
}
async _updateStats() {
//logic in this function calls updateData() if needed.
}
async _updateData() {
//Update the data
}
render() {
return (
// Component JSX ommitted from this example
);
}
}
Any ideas on what needs to be done?

I found the solution on Andrei Pfeiffer's blog: https://itnext.io/handle-tab-changes-in-react-navigation-v2-faeadc2f2ffe
Essentially I changed the navigation options to the following code:
static navigationOptions = () => ({
async tabBarOnPress({ navigation, defaultHandler }) {
await navigation.state.params.onTabFocus();
defaultHandler();
},
});
onTabFocus() now does the same work that updateStats() used to do.

Related

UI Kitten functional component - Props undefined

I'm developing a react native mobile app using UI Kitten. I'm still fairly new to both react native and UI kitten, so I am using the just the plain Javascript template VS the Typescript template.
I have a functional component screen as shown below. This screen is working just fine. Today I started REDUX implementation.
const RequestScreen = ({ navigation }) => {
// code removed for brevity
}
Within this screen I use the useEffect hook to fetch data from my API
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
getServices();
});
return () => {
unsubscribe;
};
}, [navigation]);
const getServices = async () => {
setLoading(true);
// helper function to call API
await getAllServices().then((response) => {
if (response !== undefined) {
const services = response.service;
setServices(services);
setLoading(false);
}
});
// update redux state
props.getAllServices(services);
};
// code removed for brevity
const mapStateToProps = (state) => state;
const mapDispatchToProps = (dispatch) => ({
getServices: (services) =>
dispatch({
type: Types.GET_SERVICES,
payload: { services },
}),
});
const connectComponent = connect(mapStateToProps, mapDispatchToProps);
export default connectComponent(RequestScreen);
On this line of code:
props.getAllServices(services);
I keep getting this error:
[Unhandled promise rejection: TypeError: undefined is not an object
(evaluating 'props.getAllServices')] at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:293:29 in invoke
Anytime I try to use "props" in code here. I run into errors. How do I get the props on this screen?
I tried changing the screen, as shown below, but that does not work either!
const RequestScreen = ({ navigation, props }) => {
// code removed
}
I was able to get the props object after changing the screen component as shown below.
const RequestScreen = ({ navigation, ...props }) => {}

React Navigation: Navigating outside a component

I am trying to navigate my app from outside of a component. Specifically, I am using a fetch interceptor and I want to navigate whenever an error response is received.
I followed the example here: https://reactnavigation.org/docs/navigating-without-navigation-prop/
However, my app is still giving me an error saying that either a navigator isn't rendered or the navigator hasn't finished mounting:
Screenshot of app with error message
As far as I can tell, neither of those situations apply. The app is loaded and rendered with a navigator in place before I try to actually navigate
My App.jsx:
// ... imports and so on ...
fetchIntercept.register({
response: (response) => {
if (response.status === 401) {
// Unverified subscription
RootNavigation.reset({ index: 0, routes: [{ name: 'Intercept' }] });
}
return response;
},
});
{ ... }
const InterceptNavigator = createStackNavigator(
{
Application: {
screen: ApplicationScreen,
},
Intercept: {
screen: SubscriptionInterceptScreen,
},
},
{
initialRouteKey: 'Application',
},
);
const App = createAppContainer(InterceptNavigator);
export default () => {
React.useEffect(() => {
RootNavigation.isMountedRef.current = true;
return () => { RootNavigation.isMountedRef.current = false; };
}, []);
return (
<NavigationContainer ref={RootNavigation.navigationRef}>
<App />
</NavigationContainer>
);
};
RootNavigation.js:
import * as React from 'react';
export const isMountedRef = React.createRef();
export const navigationRef = React.createRef();
export function navigate(name, params) {
if (isMountedRef.current && navigationRef.current) {
navigationRef.current.navigate(name, params);
}
}
export function reset(options) {
if (isMountedRef.current && navigationRef.current) {
navigationRef.current.reset(options);
}
}
I also inserted a number of console logs throughout and all of them showed that the app is loaded, that the navigationRef is current, and that the isMountedRef is also current before the app tries to navigate
Try .resetRoot() instead of .reset(). I think .reset() needs a state as an argument.
Found the solution. The issue is that I had a mixture of version 4 and version 5 code (and was referring to mixed documentation).
To fix the issue I removed references to version 5 code and then followed the steps on this page to get the navigator working: https://reactnavigation.org/docs/4.x/navigating-without-navigation-prop/

Undefined is not an object (evaluating T.State) - React Native

I can't seem to load my app since this error appears. I think it has something to do with react-navigation?
Here is the snippet where I used createStackNavigator
const MyTracker = createStackNavigator(
{
Games: {screen: Games},
Standings: {screen: Standings}
},
{
initialRouteName: 'Games',
transitionConfig: () => ({
transitionSpec: {
duration: 0
}
})
});
const MainContainer = createAppContainer(MyTracker);
export default MainContainer;
I solved this by downgrading my react-navigation to 2.0.0
I never ran on this problem with react-navigation, i think it´s because transitionConfig is a function, Yes, but it should RETURN something don't you know?
try this
transitionConfig: () => (
return {
transitionSpec: {
duration: 0
}
})

How can I change a parent state through a react navigation component?

I'm new to React Native and having trouble figuring out how to accomplish this. Currently I have an app structure something like this:
App.js -> Authentication.js -> if(state.isAuthenticated) Homepage.js, else Login.js
I'm currently changing the isAuthenticated state on a logout button on the homepage. I'm now trying to add in a drawer navigator to the app, which would get returned to the authentication page in place of the homepage. So I'm not sure how to pass the state change through the drawernavigator component to the Authentication page.
Currently my Homepage has a button that has:
onPress={() => this.props.logout()}
And the authentication page has:
export default class Authentication extends Component {
constructor(props){
super(props);
this.state = {
isAuthenticated: false,
isLoading: false
}
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
}
login() {
AsyncStorage.setItem("user", JSON.stringify({email: this.state.email, password: this.state.password}))
.then(results => {
this.setState({isAuthenticated: true});
});
}
logout() {
AsyncStorage.clear()
.then(result => {
this.setState({isAuthenticated: false});
});
}
componentDidMount(){
this.setState({isLoading: true});
AsyncStorage.getItem("user")
.then(results => {
const data = JSON.parse(results);
if (data) {
this.setState({isAuthenticated: true});
}
this.setState({isLoading: false});
});
}
render() {
if (this.state.isLoading){
return(
<Splashpage />
);
}
if (!this.state.isAuthenticated){
return (
<Login login={this.login}/>
);
}
return (
<Homepage logout={this.logout}/>
);
}
}
So I made a Navigation.js page where I'm creating a drawernavigator and going to be returning this instead of the Homepage.
export default Navigation = createDrawerNavigator({
Home: {
screen: Homepage,
},
WebView: {
screen: WebView,
},
});
But I'm not sure how to pass along the state change from the homepage, through the Navigation component to the parent Authentication page. Any help would be much appreciated.
You could pass a callback through navigate:
this.props.navigation.navigate('yourTarget',{callback:function});
In yourTraget you can access it via:
this.props.navigation.state.params.callback(isAuthenticated)
Here the documentation: https://reactnavigation.org/docs/en/params.html
I hope this is what you were looking for! Oh now I see you asked that already a while ago. Maybe you already moved on...

sending the navigation params as props to other component

I am using RN v0.46.4 , react-navigation and sendbird.
I am developing a chat application.
What I am trying to do is that navigating the user to Msg screen with two params item (contain user info) and createdChannel .
They are passed but only once i.e. each time I navigate to Msg screen for different users , I receive the same value on which I have presses first.
Chat Screen
_chat(item){
// console.log(item);
const { navigate } = this.props.navigation
var userIds = [item.id,1];
sb = new SendBird({appId: APP_ID});
sb.connect(1, function(user, error) {
//console.log(user);
sb.GroupChannel.createChannelWithUserIds(userIds, true, item.firstname, function(createdChannel, error) {
if (error) {
console.error(error);
return;
}
//console.log(createdChannel);
navigate('Msg', { item, createdChannel })
});
Also, when I console createdChannel in _chat function , it gives the roght information as expected.
But when I console it in Msg screen , I receive only the first createdChannel created, already told above.
Msg Screen
super(props);
console.log(props.navigation.state.routes[1].params.createdChannel);
My router structure:
const CustomTabRouter = TabRouter(
{
Chat: {
screen: ChatStack,
path: ""
}
}
ChatStack
const ChatStack= StackNavigator({
Chat:{screen: Chats,
navigationOptions: {
header: null,
}},
Msg: {
screen: Msg,
navigationOptions: ({ navigation}) => ({
title: `${navigation.state.params.item.firstname} ${navigation.state.params.item.lastname}`,
tabBarVisible: false
})
},
})
In your Msg screen's constructor function, try accessing the item and createdChannel by calling props.navigation.state.params.createdChannel.
super(props);
console.log(props.navigation.state.params.createdChannel);
console.log(props.navigation.state.params.item);
I found myself in a similar situation. In my case the problem was because passing params to nested screen was intentionally removed from the lib (1.0.0-beta21 to 1.0.0-beta22).
Note: But as of now, react-navigation version is v1.5.2
https://github.com/react-navigation/react-navigation/issues/3252
Anyway, my quick and dirty solution is to use screenProps to inject props into screen.
const ChatStack= StackNavigator({
Chat:{ screen: Chats, ... },
Msg: { screen: Msg, ... },
});
// Instead of exporting ChatStack directly, create a wrapper and pass navigation.state.params to screenProps
export default (props) => {
const params = props.navigation.state.params;
// const params = _.get(props, 'navigation.state.params');
return (
<ChatStack screenProps={params} />
);
};
Now all screens under ChatStack can access those props injected.