React Native Redux conditional rendering - react-native

I'm trying to render 2 different screens in my App based on the redux store status.
const App = () => {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
{store.getState().auth.id ? (
<Home />
) : (
<Stack.Navigator screenOptions={HeaderStyles}>
<Stack.Screen
name='Splash'
component={Splash}
options={{ headerShown: false }}
/>
<Stack.Screen name='SignUp' component={SignUp} />
<Stack.Screen name='Login' component={Login} />
</Stack.Navigator>
)}
</NavigationContainer>
</Provider>
);
};
At startup store.getState().auth.id is undefined, so the app correctly shows the Splash screen. In this screen, I set the auth.id in the store but the App doesn't rerender and stays in the Splash screen. If I save any source file without changing anything, if forces a rerender and I get correctly routed to Home.
How can I force the ternary operator to be evalued again when the store changes?

At your file, store is imported from the file it is creating in, and not injected through props (mapState || useSelector).
Because React re-render built on changes in props || state, the component doesn't know it should render again.
The easy way to go around the issue is to declare the conditional part at the child component and inject the redux state properly.
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<Navigator />
</NavigationContainer>
</Provider>
....
const Navigator = () => {
let { id } = useSelector(state => state.auth)
return !!id ? (
<Home />
) : (
<Stack.Navigator screenOptions={HeaderStyles}>
<Stack.Screen
name='Splash'
component={Splash}
options={{ headerShown: false }}
/>
<Stack.Screen name='SignUp' component={SignUp} />
<Stack.Screen name='Login' component={Login} />
</Stack.Navigator>
)
}

Related

How to create a menu Icon in a drawer navigation? React Native

I nested a Drawer Navigator in a Stack Navigator, it works well and opens when I swipe it, I want to put a Menu Icon above it when it's pressed the drawer gets open. Every method I tried always ends up with navigation can't be found error.
Here's my code:
export class App extends Component {
.....
function DrawerNav() {
return (
<Drawer.Navigator
drawerType="front"
initialRouteName="Main" drawerPosition="right">
<Drawer.Screen name="Main" component={MainScreen} />
<Drawer.Screen name="Wallet" component={WalletScreen} />
<Drawer.Screen name="Appointments" component={Appointments} />
</Drawer.Navigator>
);}
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Menu">
<Stack.Screen name="Menu" component={DrawerNav} />
<Stack.Screen name="Add" component={AddScreen} navigation={this.props.navigation}/>
<Stack.Screen name="Save" component={SaveScreen} navigation={this.props.navigation}/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
)}}
I want to open and close the drawer when I press on this icon:
<MaterialIcons name='menu' size={28} onPress={??} />
<MaterialIcons name='menu' size={28} onPress={()=>this.props.navigation.openDrawer()} />

Will a re-render of App() automatically re-render screens in navigation?

In the code below, if setStateVar is called but it doesn't affect which screens are displayed by the stacknavigator, will the displayed screen re-render or not? And if yes, is there a way to change this?
function App() {
const {stateVar, setStateVar} = useContext(VarContext);
return (
<NavigationContainer>
<Stack.Navigator>
{stateVar == null ? (
<>
<Stack.Screen options={{headerShown:false}} name="LoadingScreen" component={LoadingScreen} />
</>
) : (
<>
<Stack.Screen options={{headerShown: false}} name="SignUpScreen" component={SignUpScreen} />
</>
)
</Stack.Navigator>
</NavigationContainer>
);
}
Most likely it will. Even if you wrapped your components in React.memo(), if they use that context somewhere in their graph, they will rerender at the highest graph node that uses said context.

In react navigation how can we replce drawer screen by stack screen where stack is in drawer? Not same screens (react native)

After successfull loging im moving to this drawer.
const SalesRepDrawerNavigation = () => {
return (
<SalesRepDrawerNavigator.Navigator initialRouteName="SalesRepDashboardStack">
<SalesRepDrawerNavigator.Screen
name="SalesRepDashboardStack"
component={SalesRepDashboardStackNavigation}
options={{
headerTitle: 'Sales Rep Dashboard'
}}
/>
<SalesRepDrawerNavigator.Screen
name="Customers"
component={CustomerListScreen}
/>
<SalesRepDrawerNavigator.Screen
name="SalesOrderView"
component={SalesOrderStackNavigation}
options={{
}}
/>
</SalesRepDrawerNavigator.Navigator>
);
};
const SalesRepDashboardStackNavigation = () => {
return (
<SalesRepDashboardStackNavigator.Navigator key="test" headerMode="none" initialRouteName="SalesRepDashboard">
<SalesRepDashboardStackNavigator.Screen
name="SalesRepDashboard"
component={SalesRepDashboardScreen}
/>
<SalesRepDashboardStackNavigator.Screen
name="CreateSalesOrder"
component={CreateSalesOrderScreen}
/>
</SalesRepDashboardStackNavigator.Navigator>
);
};
then when i go to customer list screen and select a customer I want to move to create sales order screen and at the same time want to remove customer list route. I'm using react native with react navigation v5.
How can I do this?
Use it like:
In DrawerData component you will have to create a drawer item list (a custom component) and for navigation between the screens normally use the navigation.navigate('YOUR_SCREEN_NAME');
Add all your screens in the Stack.Navigator only. Drawer will have only the Home screen.
/*Drawer data is custom drawer item list*/
export function DrawerNav() {
return (
<Drawer.Navigator drawerContent={props => <DrawerData {...props} />}>
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
);
}
const RootStackScreen = () => {
return (
<Stack.Navigator>
<>
<Stack.Screen
name="HomeScreen"
component={DrawerNav}
/>
<Stack.Screen
name="FirstScreen"
component={FirstScreen}
/>
<Stack.Screen
name="SecondScreen"
component={SecondScreen}
/>
</Stack.Navigator>
);
}

React Native Navigation pass dynamic data when rendering

changeTheme(){
this.setState({darktheme:!this.state.darktheme})
}
render()
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} darkT={this.state.darktheme==true} />
<Stack.Screen name="Settings" component={SettingsScreen} changeTheme={this.changeTheme} darkT={this.state.darktheme==true} />
</Stack.Navigator>
</NavigationContainer>
);
}
In the settings page. I want to change the theme using this.props.changeTheme() it is supposed to change the state of the app and the whole app will re-render. But it doesn't work like that, the theme is not being changed. What am I doing wrong here?
When I log this.props.darkT it returns false even after I called the function changeTheme()
You can try this:
<Stack.Screen name="Settings">
{ () => <SettingsScreen darkT={!!this.state.darkTheme} changeTheme={this.changeTheme} /> }
</Stack.Screen>
In the Settings screen darkT and changeTheme will be available as props so you can access with this.props.darkT and this.props.changeTheme

Got both 'component' and 'children' props for the screen 'Search'. You must only pass one of them

I work on a react-native app and this project used react-navigation 4.x to navigate around the app.
I recently upgraded the project to 5.x of react-navigation and while trying to upgrade I ran into a problem. The problem is that my project has both a FooterNavigator and a DrawerNavigator, they both call on the same component.
We already figured out a way to fix the problem in react-navigation 4.x but the new version of react-navigation requires a name and a component for each Screen. Is there any way for me to have both the navigators at the same time or is it better to downgrade?
Image of the error
This is my FooterNavigator
const Tab = createBottomTabNavigator();
export const FooterNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Search" component={Search}>
<Button>
<Icon name="magnify" type="MaterialCommunityIcons"/>
<Text style={footerStyle.footerText}>Zoeken</Text>
</Button>
</Tab.Screen>
<Tab.Screen name="Count" component={Count}>
<Button>
<Icon name="counter" type="MaterialCommunityIcons"/>
<Text style={footerStyle.footerText}>Tellen</Text>
</Button>
</Tab.Screen>
<Tab.Screen name="Identify" component={Identify}>
<Button>
<Icon name="file-question" type="MaterialCommunityIcons"/>
<Text style={footerStyle.footerText}>Herken</Text>
</Button>
</Tab.Screen>
<Tab.Screen name="Program" component={Program}>
<Button>
<Icon name="chip" type="MaterialCommunityIcons"/>
<Text style={footerStyle.footerText}>Wijzig</Text>
</Button>
</Tab.Screen>
</Tab.Navigator>
)
}
And this is my DrawerNavigator
export const RootNavigator = () => {
let DrawerScreens = [];
Routes.forEach(function (route) {
DrawerScreens.push(<Drawer.Screen name={route.name} component={route.component}/>)
});
return (
<Drawer.Navigator>
{DrawerScreens}
</Drawer.Navigator>
)
}
They are both called and rendered in my Layout.js
render() {
return (
<NavigationContainer>
<RootNavigator />
<FooterNavigator/>
</NavigationContainer>
)
}
Many thanks in advance !!
use this
<Stack.Screen name="Home" component={HomeScreen} />
instead of this
<Stack.Screen name="Home" component={HomeScreen}> </Stack.Screen>
solve your problem
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen}/>
<Stack.Screen name="Details" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
Just remove component prop from stack screen. if you are passing custom values through navigation stack
I get this error when I use a <Stack.Screen> and provide a component and a child to it, as the error mentions.
<Stack.Screen name="Home" component={Home}>
{props => <Home {...props} sampleProperty="XXXXXXXX" />}
</Stack.Screen>
removing this bit component={Home} on line1 fixes the error.