How to pass props to Screen component with a tab navigator?
Asked Answered
A

12

48

This is my first post on StackOverflow, so apologies if I'm not following the correct format.

I'm building my first app using tab navigator from React Navigation v 5.x and have run into a big problem when passing props from one screen to another

What I want to achieve is to:

  1. Make changes to a list of data in one of my screens.
  2. Have those changes affect what happens on another screen.

I have tried this (I failed to set the props to pass down), this (Deprecated version of react-navigation) and this (Also old version of react-navigation).

and Redux but there are no examples available with the current version of react navigation.

I'm at the end of my rope with this and really need some step by step on how to achieve this. Here's a rough sketch of what I want to do:

enter image description here

The way I thought about it is sending the parent state down as props via callback, but I can't find a way to send props through the screen components in the up-to-date version of react navigation...

This is my navigation setup:

const Tab = createBottomTabNavigator()

export default class MyApp extends Component{

    constructor(props) {
        super(props);
    }

    render(){
        return (
            <NavigationContainer>
                <Tab.Navigator 
                    screenOptions={({ route }) => ({
                        tabBarIcon: ({ focused, color, size }) => {
                            let iconName;

                            if (route.name === 'My tests') {
                                iconName = focused ? 'ios-list-box' : 'ios-list';
                            } else if (route.name === 'Testroom') {
                                iconName = focused ? 'ios-body' : 'ios-body';
                            }

                            return <Ionicons name={iconName} size={size} color={color} />;
                        },
                    })}
                    tabBarOptions={{
                        activeTintColor: 'tomato',
                        inactiveTintColor: 'gray',
                            
                    }}>

                    <Tab.Screen name="My tests"  component={MyTests}/> //This is where I want to send props
                    <Tab.Screen name="Testroom" component={TestRoom} />  //This is where I want to send props
                    
                </Tab.Navigator>
            </NavigationContainer>
        )
    }
}

I've read this but it makes no sense to me. How does this fit into a tree of components? What goes where? Please help!

Architectonics answered 27/2, 2020 at 18:0 Comment(4)
Does this answer your question? React Native - pass props from One screen to another screen (using tab navigator to navigate)Exciting
@CaseSilva see if this helps you https://mcmap.net/q/357048/-react-native-pass-props-from-one-screen-to-another-screen-using-tab-navigator-to-navigateExciting
Not really since I would need to create the component that uses the data in the main App component and I don't think it should be created explicitly there. The component that needs the value of the state also needs to communicate with its parent component which is further down the stack/tab navigation tree.Scagliola
I had a very similar situation and ended up using React's Context API. Check to see if this will work for your needs: https://mcmap.net/q/357050/-how-to-pass-current-state-of-app-to-tab-navigation-screenScagliola
G
84

you can use the property 'children' to pass an element type jsx like instead of the property 'component', is recomended from react-native.

On this way you can pass props to component example:

    <tab.Screen
    name="home"
       children={()=><ComponentName propName={propValue}/>}
    />

is neccesary to use '()=>' because the property children need a function that return a jsx element, it's functional.

Gabrila answered 26/6, 2020 at 20:8 Comment(1)
inorder to have access to the navigation props, it is advisable you use it this way <Tab.Screen name="home" children={props => <Component user={user} {...props} />} />Biffin
S
20

Check out the answer in the code comments.

<Tab.Screen
  name="AdminTab"
  children={() => <AdminPage userData={this.props.userSettings} />}
  // component={() => <AdminPage userData={this.props.userSettings} />} <<<---- Although this will work but passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour. You can safely remove the component attribute post adding children.
/>

Looks like you're passing an inline function for 'component' prop for the screen 'AdminTab' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.

Stercoricolous answered 23/8, 2020 at 20:0 Comment(0)
S
18

React Navigation version 5+

<Tab.Screen name="Work" component={Works} initialParams={{ age: 45 }} />

Can be accessed using props on the Works component.

Stenotype answered 28/8, 2021 at 11:19 Comment(1)
You the man now, Dawg! This was the trick. Thanks. Just FYI, inside of the Works Component, you can access age (or any initialParams) via this.props.route.params.age.Pinite
V
9

In order to send props to <MyTests /> component, inside <Tab.Screen />:

<Tab.Screen name="My tests">
  {() => <MyTests myPropsName={myPropsValue} />}
</Tab.Screen>
Valdovinos answered 6/5, 2020 at 11:4 Comment(3)
<Tab.Screen name="My tests"> {(navigation) => <MyTests {...navigation} myPropsName={myPropsValue} />} </Tab.Screen>Pentameter
I get an error, 'Got an invalid value for 'children' prop for screen 'xxxx'. It must be a function returning a React ElementLauds
As you can see, it is a function that return a React Element.Valdovinos
M
3

I also need to pass a function to screen in order to render the popup on all screens but it is required to show the popup on a specific screen. here is my solution. Now forget about the re-render (performance) issue.

       <Tab.Screen
        initialParams={{
            onRemovePress: () => {
              props.setShowPopup(true)
            }
          }}
        name="Followers"
        component={Followers}
        options={{
          tabBarLabel: ({ focused, color }) => {
            return (
              <Text style={{ color: focused ? light.tintColor : light.black, marginBottom: 10 }}>{`${props.numOfFollowers} Followers`}</Text>
            )
          }
        }}
      />
Minelayer answered 7/2, 2021 at 11:47 Comment(0)
A
2
<Tab.Navigator>
   <Tab.Screen name="Lead">
   {() => <LeadList {...this.props} />}
   </Tab.Screen>

   <Tab.Screen name="Opportunity">
   {() => <OpportunityList {...this.props} />}
   </Tab.Screen>
</Tab.Navigator>
Astronaut answered 20/9, 2021 at 9:10 Comment(0)
C
0

you can use the following

<Tab.Screen name="Favourite" component={() => <RecipeList CategoryId={0}></RecipeList>}/>

You may gate a warning loss of current state due to re-render the component. but the props will work. If you want to copy current state props then you can use {...props}

Colter answered 19/6, 2020 at 7:8 Comment(1)
Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.Stercoricolous
K
0
<Tab.Screen
   name='tabName'
   children={()=>{
    return(
      <CustomComponent prop={yourProp} />
    )
   }}
  />

Was looking into this first time today, and found it very helpful just wanted to give small update, for @react-navigation/bottom-tabs, v^5.11.10, you need to make sure you are no longer passing in the component prop in Tab.screen and make sure the anonymous function in children, returns an element/component.

Kodiak answered 22/6, 2021 at 16:10 Comment(1)
Your code snippet isn't runnable, please edit it to show only as code formatted textArtilleryman
P
0

In case you are using ScreenMap to render screens you could do it like this:

_renderScene = SceneMap({
    first: () => <MyEvents {...this.props} date={this.state.date} />,
    second: () => <F4FEvents {...this.props} date={this.state.date} />,
});
Pyrone answered 26/7, 2021 at 5:50 Comment(0)
K
0

if you navigate from one tab to another and not updating please try this method this will help you

 <Tab.Screen
    name="Transaction"
    children={() => <GoldTransaction data={true} />}/>
Krypton answered 15/2, 2023 at 7:0 Comment(0)
I
0

this works in JS ,but not works in TS. Because TS expects type comes from property 'children' which is declared here on type children: (props: { route: RouteProp<ParamList, RouteName>; navigation: any;› }) => React.ReactNode;

export declare type ScreenListeners<State extends NavigationState, EventMap extends EventMapBase> = Partial<{
    [EventName in keyof (EventMap & EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
}>;
declare type ScreenComponentType<ParamList extends ParamListBase, RouteName extends keyof ParamList> = React.ComponentType<{
    route: RouteProp<ParamList, RouteName>;
    navigation: any;
}> | React.ComponentType<{}>;
export declare type RouteConfigComponent<ParamList extends ParamListBase, RouteName extends keyof ParamList> = {
    /**
     * React component to render for this screen.
     */
    component: ScreenComponentType<ParamList, RouteName>;
    getComponent?: never;
    children?: never;
} | {
    /**
     * Lazily get a React component to render for this screen.
     */
    getComponent: () => ScreenComponentType<ParamList, RouteName>;
    component?: never;
    children?: never;
} | {
    /**
     * Render callback to render content of this screen.
     */
    children: (props: {
        route: RouteProp<ParamList, RouteName>;
        navigation: any;›
    }) => React.ReactNode;
    component?: never;
    getComponent?: never;
Ignacia answered 24/2, 2023 at 2:7 Comment(1)
i resolved by this method in TS > get a navigation prop for your tabBar reactnavigation.org/docs/material-top-tab-navigatorIgnacia
A
0

You can use params to get this done. To set the initial params or props you can use initialParams like this:

<Tab.Screen
    name="Home"
    component={HomeScreen}
    options={{
        initialParams={
            name: 'Your name'
        }
    }}
/>

Then you can access the params using the prop route like this:

const HomeScreen = ({ route }) => (
    <View>
        <Text>Welcome to my app, {route.params.name}</Text>
    </View>
)

or using the useRoute hook:

const HomeScreen = () => {
    const { params } = useRoute()
    return (
        <View>
            <Text>Welcome to my app, {params.name}</Text>
        </View>
    )
}

Update: You can also use the prop navigation to access the param by calling navigation.getParam() like this:

const HomeScreen = ({ navigation }) => (
    <View>
        <Text>Welcome to my app, {navigation.getParam('name', 'Anonymous')}</Text>
    </View>
)

navigation.getParam() takes two arguments, the name of the param and the default value. You can do the same thing with the useNavigation hook.

You can even pass params when you navigate from another screen by passing the to the second argument of navigation.navigate:

const LoginScreen = ({ navigation }) => {
    const [name, setName] = useState('')
    return (
        <View>
            <TextInput 
                placeholder="Type your name here"
                value={name}
                onChangeText={(text) => setName(text)}
            />
            <Button 
                title="Submit"
                onPress={() => navigation.navigate('Home', {
                    /* Pass your params here */
                    name
                })}
            />
        </View>
    )
}

You can also change the params within the HomeScreen component using navigation.setParams and passing in the new params:

const HomeScreen = ({ route, navigation }) => (
    <View>
        <Text>Welcome to my app, {route.params.name}</Text>
        <Button 
            title="Reset name"
            onPress={() => navigation.setParams({
                name: ''
            })}
        />
    </View>
)

Note: You can only access the route and navigation props if the component is a page used in the navigator.

See more about passing params at https://reactnavigation.org/docs/params

Although this is the more popular approach, there are other ways to get this done such as passing the children attribute of Tab.Screen an inline component, or putting the screen data in an application state. You can chose which approach you want to take, but React Navigation params are probably your best option.

Auric answered 6/3 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.