i have code like below
const Project = ({ navigation, route }) => {
const id = route.params.itemId;
return (
<Stack.Navigator >
<Stack.Screen
name="ProjectMain"
component={TopNav}
initialParams={{ id: id }}
/>
</Stack.Navigator>
);
};
Now how do i pass initialParams to my component TopNav
my TopNav is like below
const TopNav = (props) => {
console.log(props);
console.log(props.route);
return (
<>
//my tab screens
</>
);
};
I am not able to get the params here , its always undefined ,how do i pass something to my TopNav component , thank you
This looks correct to me.
Can you double check that id (line 2 in Project) is not undefined?
What is the output of props.route? It should be an object with key, name, params, and path
Can you try using:
const TopNav = () => {
const route = useRoute()
console.log(route);
return (
<>
//my tab screens
</>
);
};
Use options.
<Stack.Screen
name="ProjectMain"
component={TopNav}
options={{
id: 'Awesome app',
}}
/>
Related
For some reason, I just can't get JSON strings to display in my component. I've tried various solutions, but no luck.
Below is my component where I map over my items:
UnitEventsItems.js
const GetEventsItems = (props) => {
const { parsed = {} } = props;
const getEventsItems = Object.entries(parsed).map((item, i) => ({
item_Id: i,
label: item[0],
info: item[1]
}));
return (
<div style={{
marginBottom: theme.spacing(4)
}}
>
{getEventsItems.map((eventsItems) => (
...
<Typography>
{`${eventsItems.info}`}
</Typography>
</Box>
Below is the component where I import getEventItems for display:
EventsBlock.js
import GetEventsItems from './UnitEventsItems';
...
const EventsBlock = (props) => {
const classes = useStyles(props);
const {
eventsData,
type
} = props;
return (
<>
{
...
<Paper className={clsx(classes.paper, classes.scrollBar)}>
<List component="nav" aria-label={`${type} Events`}>
{eventsData.map((item, i) => (
<ListItem key={`${i + 1}${item.type}_${item.trixid}`}>
<GetEventsItems
parsed={item.comment}
/>
</ListItem>
))}
</List>
</Paper>
And then this is the parent where I import EventBlock.js and pass the props:
index.js
import React from 'react';
import EventsBlock from './EventsBlock';
const UnitEvents = (props) => {
const { eventsData } = props;
const refueling = (eventsData?.Refueling ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
const periodic = (eventsData?.Periodic ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
const corrective = (eventsData?.Corrective ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
console.log('periodic:', periodic);
console.log('refueling:', refueling);
console.log('corrective:', corrective);
return (
<>
<EventsBlock type="Periodic" eventsData={periodic} />
<EventsBlock type="Refueling" eventsData={refueling} />
<EventsBlock type="Corrective" eventsData={corrective} />
</>
);
};
export default UnitEvents;
Below is an image of what data looks like coming through from the API:
Below is what I have logged in the console as shown in the code above:
This is live example of code Sandbox
What is wrong on your code
In the EventsBlock component you need to have a check Object.keys(eventsData).length after that Draw eventsData.map
Inside of eventsData.map you need also some check before draw GetEventsItems , something like
{ item.comment && (
<li>
<GetEventsItems parsed={item.comment } />
</li>
)
Check this example , I have wrote your logic with other json data , try to use that aproach with your json data. code
Problem solved by making the following the change.
Changed this:
<GetEventsItems parsed={item.comment} />
to this:
<GetEventsItems parsed={item}/>
I have 2 different components AnimeList and MangaList that share similar logic, so I want to share code between them. Here is how I'm currently doing it:
const AnimeList = (props) => <AnimangaList isAnime={true} {...props} />;
const MangaList = (props) => <AnimangaList isAnime={false} {...props} />;
return (
<Tab.Navigator>
<Tab.Screen name="Anime" component={AnimeList} />
<Tab.Screen name="Manga" component={MangaList} />
</Tab.Navigator>
);
Is there a shorter or more convenient way to do this in React / React Native? ie. Something similar to function.bind()? This feels pretty hefty.
I usually go with static array when I need to have different values for few properties on large amount of components.
const pages = [
{
isAnime: true,
name: 'Anime',
},
{
isAnime: false,
name: 'Manga',
},
];
// mocks for some components
const MangaList = ({ isAnime }) => <div>{isAnime}</div>;
const Screen = ({ name, component: Component }) => (
<div>
{name}
<Component />
</div>
);
And then inside Tab.Navigator:
{pages.map((p, i) => (
<Screen key={i} name={p.name} component={(props) => <MangaList isAnime={p.isAnime} {...props} />} />
))}
I have a two menu components: profile-menu and main-menu.
Each menu has links with icons and component-button to open it.
Is it good idea to pass multiple components as links array and than render it with map like in this example? if not, what is alternative?
const Icon = () => <div>1</div>
const Icon2 = () => <div>2</div>
const links = [{ href: '/', name: 'MainPage', icon: Icon }, { href: '/test', name: 'Test', icon: Icon2 }]
const MainMenu = () => {
return (
<Navigation side='left' links={links}>
<img src='smt..' />
</Navigation>
)
}
const Profile = () => {
return (
<Navigation side='right' links={links}>
<img src='smt..' />
</Navigation>
)
}
const Navigation = ({ side, links, children }) => {
const menuRelayRef = useRef(null) // to close menu on page click
// some logic
return (
<Fragment>
{cloneElement(children, { ref: menuRelayRef })}
<div show={show} side={side}>
{links.map((l) => {
const Icon = l.icon // is it ok?
return (
<div>
<button>{l.name}</button>
<Icon />
</div>
)
})}
</div >
</Fragment>
)
}
i think it depends on your Icon component :
if Icon component is img tag so you should pass only src,alt.
if Icon component is SVG icon so you should pass SVG icon component.
generally if your component has same parts, so try to DRY (Dont Repeat Yourself),
and pass only dynamic parts of component.but sometimes you have no other way and passing component is best solution.
I’m using materialTopTabs and it seems like this loads all the screens in the navigator once its mounted. I have a screen List and inside it a tab navigator with 2 screens: Posts and Users. These two screen both depend on params passed from List. However, i am only able to pass params to one of the screens using this method:
navigation.navigate('PostsTabNav', {
params: {
network: item,
},
screen: 'NetworkPosts' //or NetworkUsers
});
I have tried to pass the params to my navigator directly by doing this:
navigation.navigate('PostsTabNav', {
network: item
});
The first option only allows me to pass to one screen. The second option allows me to access the params inside the navigator like this:
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = (props) => {
const temp = props.route.params.network; //params here
return (
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} />
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} />
</PostsTabNav.Navigator>
);
};
Is there a way to pass temp to both my screens? If not is there a better way to handle this situation?
Here's the code for the StackNavigator
const NetworkListStackNav = createStackNavigator();
export const NetworksListNavigator = () => {
return (
<NetworkListStackNav.Navigator>
<NetworkListStackNav.Screen name="List" component={ListScreen} />
<NetworkListStackNav.Screen name="PostsTabNav" component={PostsMainNav} />
</NetworkListStackNav.Navigator>
);
};
Pass params to the navigator and then expose it to the tabs using React Context.
Create a context in a separate file which you can import in both your navigator and screens:
export const NetworkContext = React.createContext();
Then provide the params in the context:
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = ({ route }) => {
return (
<NetworkContext.Provider value={route.params.network}>
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} />
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} />
</PostsTabNav.Navigator>
</NetworkContext.Provider>
);
};
In your screen component, use the context:
const network = React.useContext(NetworkContext);
Also see https://reactnavigation.org/docs/hello-react-navigation#passing-additional-props
You can set initial params to your screens.
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = (props) => {
const temp = props.route.params.network
return (
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} initialParams={network:temp}/>
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} initialParams={network:temp}/>
</PostsTabNav.Navigator>
);
};
const Stack = createStackNavigator();
function AppNavigator(props) {
const { cartCount } = props;
return (
<NavigationContainer>
<Stack.Screen {...props} name="MainScreen" component={() => <MyTabs cartCounts={cartCount}></MyTabs>} options={{ headerShown: false }} countProp={cartCount} initialParams={{ 'count': cartCount }} />
</Stack.Navigator>
</NavigationContainer>
)
}
function mapStateToProps(state) {
return {
cartCount: state.cart.cartCount,
}
}
export default connect(mapStateToProps, {})(AppNavigator);
And your tab component like
function MyTabs(props) {
let cartCount = props.cartCounts;
return (
<BottomTab.Navigator
screenOptions={{
tabBarStyle: { position: 'absolute' },
tabBarStyle: styles.tabbarStyle
}}>
<BottomTab.Screen name="CartScreen" component={CartScreen} options={{ headerShown: false }}
options={{
headerShown: false,
tabBarIcon: ({ focused, tintColor }) => (
<View style={styles.cartIconView}>
<Image
square
source={bottomCart}
/>
<Badge style={[GlobalStyles.badge, styles.count, {
}]}>
<Text style={[GlobalStyles.badgeText, styles.countText]}>{cartCount}</Text>
</Badge>
</View>
),
}} />
</BottomTab.Navigator >
);
Start:
<NavigationContainer>
<Stack.Navigator>
{
isLoading ? <Stack.Screen name="AuthLoadingScreen" component={AuthLoadingScreen} />
: (
user ? (
<Stack.Screen name="AuthenticatedStack" component={AuthenticatedStack} options={{headerShown: false}} />
) : (
<Stack.Screen name="NotAuthenticatedStack" component={NotAuthenticatedStack} options={{headerShown: false}} />
)
)
}
</Stack.Navigator>
</NavigationContainer>
Authenticated stack:
const AuthenticatedStack = () => {
return (
<Drawer.Navigator initialRouteName="MainStack" drawerContent={props => <SideBar {...props} />}>
<Stack.Screen name="MainStack" component={MainStack} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
</Drawer.Navigator>
);
};
Main stack contain main screen with tab navigator and other navigators which I plan to navigate to from side menu buttons:
const MainStack = () => {
return (
<Stack.Navigator>
<Stack.Screen name="main" component={MainTabNavigator} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
<Stack.Screen options={({route}) => ({
title: getHeaderTitle(route),
})} name="Welcome" component={WelcomeStack} />
</Stack.Navigator>
);
};
Main tab navigator is just few tabs:
const MainTabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen
...
Home stack navigator:
const HomeStackNavigator = ({navigation, routes}) => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{
headerLeft: () => (),
}} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
};
Welcome stack:
const WelcomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} name="welcome" component={WelcomeScreen} />
</Stack.Navigator>
);
};
I use this two function to show hide header to avoud double headers on tabs and to set titles:
function shouldHeaderBeShown(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return false;
}
}
function getHeaderTitle(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return 'Home';
case 'Settings': return 'Settings';
case 'Welcome': return 'Welcome';
}
}
And here I got to the problem:
This line: route.state ? route.state.routes[route.state.index].name if I switch between tabs I get all correctly state property is there and it's fine titles are set.
But If I from drawer navigate to Welcome then route doesn't have state property so it always pick 'Home' for title.
I pass this by making this line to:
const routeName = route.state ? route.state.routes[route.state.index].name : route.name;
Then it is showing Welcome title.
But I don't understand is this workaround ok or this screens can be set in a better way?
But If I from drawer navigate to Welcome
How do you navigate from drawer? If you're navigating by passing screen param, you can do this:
function getActiveRouteName(route) {
// Access the tab navigator's state using `route.state`
const routeName = route.state
? // Get the currently active route name in the tab navigator
route.state.routes[route.state.index].name
: // If state doesn't exist, we need to default to `screen` param if available, or the initial screen
// In our case, it's "Home" as that's the first screen inside the navigator
route.params?.screen || 'Home';
return routeName;
}
Also seems the reason you even need this is because you have so many nested navigators. Maybe try to reduce nesting first? https://reactnavigation.org/docs/en/nesting-navigators.html#best-practices-when-nesting
I think you should use dispath not navigate.
In navigate, you can't get updated state if you navigate to that screen again.
import {StackActions} from 'react-navigation';
pushAction = StackActions.push({
routeName: 'FormsScreen',
params: {
id: result.id,
flag: true,
comments: result.note,
type: result.type,
date: result.created,
accountName: result.accountName,
accountId: result.accountId,
accountType: result.accountType,
bundleId: result.bundleId,
formAnswer: JSON.parse(result.formAnswer),
},
this.props.navigation.dispatch(pushAction);