How to pass props between functions within a React component - reactjs

I'm looking to pass a prop to a component in the header of the navigation option. Is there a way to pass itemId to SaveButton from within the component without using something like Context API or Redux?
export default PostingDetail = ({ navigation }) => {
const itemId = navigation.getParam('itemId', null)
return (
<SafeAreaView >
</SafeAreaView>
)
}
PostingDetail.navigationOptions = ({ navigation }) => ({
title: "Posting Detail",
headerRight: (
<PleaseSignIn navigation={navigation} >
<SaveButton id={itemId} />
</PleaseSignIn>
)
})

You can pass param as below :
export default PostingDetail = ({ navigation }) => {
const itemId = navigation.getParam('itemId', null);
navigation.setParams({ itemId }); // Pass itemId to navigation state
return (
<SafeAreaView >
</SafeAreaView>
)
}
And then you can get itemId as below :
PostingDetail.navigationOptions = ({ navigation }) => ({
title: "Posting Detail",
headerRight: (
<PleaseSignIn navigation={navigation} >
<SaveButton id={navigation.state.params.itemId} />
</PleaseSignIn>
)
})

Related

Send ref via props in functional component

In my parent component I call hook useRef: const flatListRef = useRef(null); and then I want to use this flatListRef in child component. I tried to do like in documentation but without success. When I call my function toTop I get: null is not an object (evaluating 'flatListRef.current.scrollToOffset')
This is my parent component:
const BeautifulPlacesCards = ({navigation}: HomeNavigationProps<"BeautifulPlacesCards">) => {
const flatListRef = useRef(null);
const toTop = () => {
flatListRef.current.scrollToOffset(1)
}
const buttonPressed = () => {
toTop()
}
return(
<Carousel filteredData={filteredData} flatListRef={flatListRef}/>
)
}
This is my child component:
const Carousel = forwardRef((filteredData, flatListRef) => {
return (
<AnimatedFlatList
ref={flatListRef}
/>
)
}
Here is a working example: https://snack.expo.dev/#zvona/forwardref-example
Key takes:
you need to use prop ref when passing it down, not flatListRef
you need to destructure filteredData from props
Here is the relevant code:
const Child = forwardRef(({ filteredData }, ref) => {
return (
<FlatList
ref={ref}
style={styles.flatList}
data={filteredData}
renderItem={({ item }) => (
<Text style={styles.item} key={`foo-${item}`}>
{item}
</Text>
)}
/>
);
});
const App = () => {
const flatListRef = useRef(null);
const toTop = () => {
flatListRef.current.scrollToOffset(1);
};
return (
<View style={styles.container}>
<Button title={'Scroll back'} onPress={toTop} />
<Child filteredData={[1,2,3,4,5,6]} ref={flatListRef} />
</View>
);
};

How to access data from FlatList in to parent component to be used in a function

i'm trying to access the code and name value from within my child component which is a flatlist and access the select data in the parent component:
My flatList is as follows & it house a child component on which it will render items to. But however i get undefind or maybe i've used the wrong approach
const CustomerView = ({ code, name, email, balance, buttonPress }: any) => {
return (
<View style={styles.body}>
<TouchableOpacity onPress={buttonPress}>
<Text>Code: {code}</Text>
<Text>Name: {name}</Text>
<Text>E-Mail: {email}</Text>
<Text>Balance: {balance}</Text>
</TouchableOpacity>
</View>
);
};
And below is my flatList component which will render the above componet when data gets passed through
const CustomerFlatList = ({
customerData,
onPressSelectCustomer,
}: any) => {
return (
<View style={styles.body}>
<FlatList
data={customerData}
keyExtractor={(customerData) => customerData.code.toString()}
//I need to access code and name in the parent component
renderItem={({ item: { code, name, email, balance } }) => {
return (
<View>
<CustomerView
code={code}
name={name}
email={email}
balance={balance}
buttonPress={onPressSelectCustomer}
/>
</View>
);
}}
/>
</View>
);
};
And my home component which is supposed to get code, name passed through in order to trigger an event with there data being passed through
const SelectCustomerScreen = ({navigation}) => {
const customers = useSelector((state: RootStateOrAny) => state.customers);
const getCustomerDetails = (code, name) => {
//navigation.navigate("orderScreen");
console.log(code, name)
}
return (
<View style={{ flex: 1 }}>
<CustomerFlatList
customerData={customers}
doc_type={documentType}
invoiceScreen={invoiceScreen}
quotationScreen={quotationScreen}
onPressSelectCustomer={getCustomerDetails}
/>
</View>
);
};

Passing params to tab navigator React Navigation 5

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 >
);

Exposing state props from functional component to navigationOptions function

I have a component that renders the input field, I want to pass the data to the next page when user clicks on "next" button in the header. What is the best practice for doing so? How do I expose this into Page.navigationOptions?
Or is it best to just set up redux for these types of things?
const Page = () => {
const [desc, getDesc] = useState('');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => getDesc(text)}
value={desc}
/>
</View>
);
};
// How do I pass desc properties down into navigationOptions?
Page.navigationOptions = (navData) => {
return {
headerTitle: 'Page,
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Next'
onPress={() => {
navData.navigation.navigate('NextPage', {data: navData});
}}
/>
</HeaderButtons>
),
headerBackTitle: null
};
};
/* NextPage.js */
const NextPage = (props) => {
console.log('enter props data', props.navigation.getParam('data'));
console.log('enter props navigation', props.navigation);
const [valueText, setValueText] = useState();
return (
<View>
<TextInput onChangeText={(text) => setValueText(text)} value={valueText}/>
<TouchableOpacity><Text>Create your workout</Text></TouchableOpacity>
</View>
);
;}
Sharing state and props between component and options is possible in React Navigation 5 https://blog.expo.io/announcing-react-navigation-5-0-bd9e5d45569e
In React Navigation 4, you can use params to store the value to be able to share it:
const Page = ({ navigation }) => {
const desc = navigation.getParam('description', '');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => navigation.setParams({ description: text )}
value={desc}
/>
</View>
);
}

In React, how can I use HOC (High Order Component) to create several components out of a generic component?

I have created a generic component to be used as a wrapper for other components to be used as labels. Here is my generic component:
const Label = ({ data, attribute, style, link }) => {
if (link) {
return (
<Link to={link} style={style}>{data ? `${data[attribute]}` : ''}</Link>
);
}
return (
<div style={style}>{data ? `${data[attribute]}` : ''}</div>
);
};
I want to use this as my generic component for rendering different label components such as:
const CityLabel = ({ data }) => (
<div>{data ? `${data.city}` : ''}</div>
)
and
const UserLabel = ({ user }) => (
<div>{user ? `${user.firstName} ${user.lastName}` : ''}</div>
)
etc...
How can I use a HOC to do this?
This example assumes UserLabel only renders name instead of firstName & lastName as your Label component cannot handle two attributes.
const Label = ...,
makeLabel = (
(Label) => (mapLabelProps) => (props) =>
<Label {...mapLabelProps(props)} />
)(Label),
CityLabel = makeLabel(({data, style, link}) => ({
data,
attribute: 'city',
style,
link
})),
UserLabel = makeLabel(({user, style, link}) => ({
data: user,
attribute: 'name',
style,
link
}));
render(){
return (
<div>
<CityLabel data={{city:"NYC"}} />
<UserLabel user={{name:"obiwan"}} />
</div>
)
}

Resources