Undefined is not an object (evaluating T.State) - React Native - 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
}
})

Related

React-native-navigation is not working correctly

I'm trying to make custom headerLeft button, But the thing is When I'm going to this specific, route without giving any errors or warnings, it is not working.
Here is my react-navigation stack
const Navigator = createStackNavigator({
Welcome: WelcomeScreen,
GetStarted: {
screen: GetStartedScreen,
},
CreatePassword: CreatePasswordScreen,
AlternatePhrase: {
screen:AlternatePhraseScreen,
navigationOptions: ({navigation}) => {
return {
headerLeft:<HeaderBackButton onPress={navigation.navigate("WelcomeScreen")} />
}
}
},
ConfirmPhrase: ConfirmPhraseScreen,
FanwallyAgreement: FanwallyAgreementScreen,
ImportWallet: ImportWalletFieldsScreen,
Main: MainScreen,
TokenDeposit,
});
Every other screens are working perfectly its just this one
`
AlternatePhrase: {
screen:AlternatePhraseScreen,
navigationOptions: ({navigation}) => {
return {
headerLeft:<HeaderBackButton onPress={navigation.navigate("WelcomeScreen")} />
}
}
},
`
Any suggestions on what am I doing wrong?
Try this
onPress={() => navigation.navigate("WelcomeScreen")}

How to combine dynamic routes and static routes in react native

I'm creating a react native wizard, and I want to combine my static route and wizard dynamic routes.
Currently I am using react navigation, and the major problem is i have a generateNavigator function that creates the dynamic routes based on the props passed down to Route.js component where the function exists. I have tried moving the function to the AppNavigator where the static routes exists but am getting navigator must be a function. I'm running the app expo
Route.js
generateNavigator = () => {
const { steps, title } = this.props;
const navigationRoutes = {};
steps.forEach((step, index) => {
const routeOptions = { screen: step.component };
navigationRoutes[step.routeName] = routeOptions;
});
const navigationOptions = {
headerStyle: {
backgroundColor: '#ffffff',
paddingHorizontal: 5,
},
headerTintColor: '#111111',
headerTitle: title,
};
return createAppContainer(createStackNavigator(navigationRoutes, { navigationOptions }));
}
AppNavigator.js
const MainStack = createStackNavigator(
{
Main: {
screen: {
MainTabNavigator,
},
},
...ModalStack
},
{
mode: 'modal',
headerMode: 'none',
}
);
export default createAppContainer(
createSwitchNavigator({
AuthLoading: AuthLoading,
Main: MainStack,
Auth: AuthStack,
}, {
initialRouteName: 'AuthLoading',
})
);
I expect the app to run without warning, but instead I'm getting a warning 'You should only render one navigator explicitly in your app'. As said earlier i tried moving the generateNavigator() to AppNavigator in order to solve the problem but with no success.

React Navigation - Jumping between tabs

Hello guys I have 5 Containers in my tab bar and I would like to know if there is an option to return to the main screen if I select another tab container.
So I have the following
https://snack.expo.io/#react-navigation/stacks-in-tabs-v3
While I am in Home tab I click on Go to Details and then I switch to Settings tab. If I return to Home container I want to see the default screen which is HomeScreen
Any option?
You can redefine the tabBarOnPress of the navigationOptions of each StackNavigator like so:
const stacks = [
{ Home: HomeScreen, Details: DetailsScreen },
{ Settings: SettingsScreen, Details: DetailsScreen }
].reduce((stacksObj, stack) => {
const initialRoute = Object.keys(stack)[0];
stacksObj[initialRoute] = createStackNavigator(
stack,
{
navigationOptions: {
tabBarOnPress: ({ navigation }) => navigation.navigate({
routeName: initialRoute,
action: navigation.popToTop()
})
}
}
);
return stacksObj
}, {});
export default createAppContainer(createBottomTabNavigator(
stacks,
{ /* same defaultNavigationOptions as the snack example */ }
));
Cause your DetailScreen is nested in stack Home, you must navigate one more level to that screen
_navigateHome = () => {
const navigateAction = NavigationActions.navigate({
routeName: 'Home',
action: NavigationActions.navigate({ routeName: 'Home' }),
});
this.props.navigation.dispatch(navigateAction);
}
Looks the api has changed , the new way to achieve this is :
import { CommonActions } from '#react-navigation/native';
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: {
user: 'jane',
},
})
);

React Navigation Warning: "jumpToIndex is not a Function"

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.

Performing action on swipe back (react-navigation)

In react-navigation, know there is a way to perform custom actions when the back button is pressed. Is there a similar way to do this when the user swipes to go back to the last screen?
I was able to determine whether the app is going back via the back button or swiping back with the getStateForAction function of the router. Play around with checking the action and state variables to fine tune it more if needed.
export const ScreensStack = StackNavigator(
{
Login: {screen: LoginScreen},
Schedule: {screen: ScheduleScreen},
}
/**
* Intercept actions occuring in the screen stack.
*/
const getStateForActionScreensStack = ScreensStack.router.getStateForAction;
ScreensStack.router = {
...ScreensStack.router,
getStateForAction(action, state) {
if (action.type == 'Navigation/BACK') {
console.log('We are going back...');
}
return getStateForActionScreensStack(action, state);
},
};
React Navigation 5.7 introduced a simple way to do this without the need to override back actions or swipe gestures, basically you intercept the back action in the component and execute a callback before it happens:
import {useNavigation} from '#react-navigation/native';
const MyScreenComponent = (props) => {
const navigation = useNavigation();
React.useEffect(() => (
navigation.addListener('beforeRemove', (e) => {
// Prevent default behavior of leaving the screen (if needed)
e.preventDefault();
// Do whatever...
})
), [navigation]);
// Rest of component
}
In this example I'm using the useNavigation hook to access the navigation object, but you can also extract it from the props, it really depends on how your app is built.
Official documentation.
Using this in react-navigation v5.0
So I am using Pan-Responder to handle swipe-back event in react-native iOS.
So how I achieved this:
in my Navigator.js I have added gestureEnabled: false functionality of the app.
const Funnel = createStackNavigator({
page1: Page1,
page2: Page2,
page3: Page3,
}, {
// headerMode: 'float',
defaultNavigationOptions: ({ navigation }) => ({
...TransitionPresets.SlideFromRightIOS,
gestureEnabled: false,
header:
(<SafeAreaView style={{ flex: 1 }} forceInset={{ bottom: 'never' }}>
<AppHeader pageData={navigation.state.params == undefined ? data : navigation.state.params} />
</SafeAreaView>),
}),
})
so by above code I was able to remove back-swipe functionality in iOS app.
now I have to add swipe-back functionality.
export let panResponder = (callback) => PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return Math.abs(gestureState.dx) > 5;
},
onPanResponderRelease: (evt, gestureState) => {
if (Platform.OS != 'ios') return
if (Math.floor(gestureState.moveX) >= 0 && Math.floor(gestureState.moveX) <= width / 2) {
callback()
}
},
});
so by using above code you will get a callback of swipe-back event in which you can add navigation.goBack() functionality.
How to add PanResponser in you view:
<View {...panResponder(backButtonCallback).panHandlers}/>
There are some examples in the react-navigation docs:
https://reactnavigation.org/docs/en/routers.html
I found that there is an immediate param on the action that is set true when the back is from swipe yet undefined in other types of back:
const MyApp = createStackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
const defaultGetStateForAction = MyApp.router.getStateForAction;
MyApp.router.getStateForAction = (action, state) => {
if (state && action.type === 'Navigation/BACK' && action.immediate) {
console.log('Perform custom action on swipe')
}
return defaultGetStateForAction(action, state);
};