Pass Props and Function in onPress React Native - javascript

I'm calling a component in my file so I made a function in parent component and want to hit this function onPress of child component MenuItem and I also want to hit hideMenu function from child component at same time. Please provide me a solution for it. Thanks
Parent Component
onView(){
alert('Dairu');
}
{this.state.clientsList.map((item) => {
return (
<View style={styles.caseItem} key={item.ID}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<CustomMenu onView={() => this.onView()} />
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
})}
Child Component
hideMenu = () => {
this._menu.hide();
};
render() {
return (
<Menu
ref={this.setMenuRef}
button={<Icon type="Feather" name="more-vertical" onPress={this.showMenu} style={{ fontSize: 20, color: '#555' }} />}
>
<MenuItem onPress={this.props.onView}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
);
}

You've already passed the onView prop to the child object. Just call this method in the hideMenu method in the child component:
hideMenu = () => {
this._menu.hide();
this.props.onView();
};

you just need to add this.props.onView() on your hideMenu function
hideMenu = () => {
this._menu.hide();
this.props.onView();
};
also, you can pass an argument to parent component like:
this.props.onView(arg1, arg2);
and in parent component:
onView={(arg1, arg2)=> onView(arg1, arg2)}

Related

How to pass onPress to props.children?

I'm trying to make a wrapper component in react-native that I can pass down all its props to the children it wraps around. What I really want is to pass down all function props down to all its children. It looks something like this below. I want the onPress in Wrapper to be called when the TouchableOpacity is pressed.
I tried this below but it doesn't work
const Wrapper = ({children,...props})=>{
return <View {...props}>{children}</View>
}
const App = ()=>{
return (
<View style={{flex:1}}>
<Wrapper onPress={()=>{console.log(2)}}>
<TouchableOpacity/>
</Wrapper>
</View>
)
}
It looks like you're looking to map the children and apply the props to each one. That might look like this:
const Wrapper = ({children,...props})=>{
return (<>
{React.Children.map(children, child => (
React.cloneElement(child, {...props})
))}
</>);
}
(method of mapping the children borrowed from this answer)
const App = () => {
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
// do the action need here here
}}>
<Wrapper >
</Wrapper>
</TouchableOpacity>
</View>
)
}
I would advise you to use hooks function instead
If you try to reuse functions that are related
** useAdd.js **
export default () => {
const addFuction(a, b) => {
// do preprocessing here
return a + b
}
return [addFuction]
}
main componet
import useAdd from "/useAdd";
const App = () => {
const [addFuction] = useAdd()
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
addFuction(4,5)
}}>
...action component...
</TouchableOpacity>
</View>
)
}
console in useAdd hook.... to see visual changes use the react useState

How do I render my dropdown menu in react-native?

I have a functional component Profile.js and on the header of that screen I put a button. Once this button is pressed I want to display a dropdown menu from react-native-material-menu
Part of my Profile.js:
function Profile(props) {
useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => <HeaderRight />,
});
}
}, []);
My HeaderRight component:
export const HeaderRight = () => {
return (
<TouchableOpacity style={{ marginTop: 20, marginRight: 8 }}>
<Feather
name="more-vertical"
size={24}
color="black"
onPress={() => renderMenu()}
/>
</TouchableOpacity>
);
};
As you can see when the icon is pressed I call my renderMenu(), which looks like the following:
import React, { useState } from "react";
import { View, Text } from "react-native";
import { Menu, MenuItem, MenuDivider } from "react-native-material-menu";
export const renderMenu = (props) => {
const [visible, setVisible] = useState(true);
const hideMenu = () => setVisible(false);
const showMenu = () => setVisible(true);
return (
<View
style={{ height: "100%", alignItems: "center", justifyContent: "center" }}
>
<Menu
visible={visible}
anchor={<Text onPress={showMenu}>Show menu</Text>}
onRequestClose={hideMenu}
>
<MenuItem onPress={hideMenu}>Menu item 1</MenuItem>
<MenuItem onPress={hideMenu}>Menu item 2</MenuItem>
<MenuItem disabled>Disabled item</MenuItem>
<MenuDivider />
<MenuItem onPress={hideMenu}>Menu item 4</MenuItem>
</Menu>
</View>
);
};
When I render my page, I click on the header icon and I get the following error:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Can you tell me, how do I render my dropdown menu component?
You do not render the JSX on the return of an onPress function. Try this out:
export const HeaderRight = () => {
const [visible, setVisible] = useState(false);
let toggle = () => setVisible(!visible);
return (
<>
<TouchableOpacity style={{ marginTop: 20, marginRight: 8 }}>
<Feather
name="more-vertical"
size={24}
color="black"
onPress={toggle}
/>
</TouchableOpacity>
<Menu
visible={visible}
anchor={<Text onPress={toggle}>Show menu</Text>}
onRequestClose={toggle}>
<MenuItem onPress={toggle}>Menu item 1</MenuItem>
<MenuItem onPress={toggle}>Menu item 2</MenuItem>
<MenuItem disabled>Disabled item</MenuItem>
<MenuDivider />
<MenuItem onPress={toggle}>Menu item 4</MenuItem>
</Menu>
</>
);
};

React Native: FlatList Opens Modal for all Items Instead of Selected Item

I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible

Not working onPress in nested TouchableOpacity

Hi my custom component is wrapped in TouchableOpacity like this.
const profileOnClick = () => {
console.log('Card Clicked!');
};
export const InfluencerCard = props => {
const {influencer, navigation} = props;
return (
<TouchableOpacity onPress={() => profileOnClick()}>
<Card>
<Text>
{influencer.user.name}
</Text>
<Text>
{influencer.tags[0].name}
</Text>
</Card>
</TouchableOpacity>
);
};
In Homescreen
<ScrollView>
{data.categoriesForHome.map(category => (
<Layout key={category.id}>
<Text>
{category.name}({category.total})
</Text>
<ScrollView horizontal={true}>
{category.influencerProfiles.map(profile => (
<View key={profile.id}>
<InfluencerCard influencer={profile} />
</View>
))}
</ScrollView>
</Layout>
))}
</ScrollView>
When I clicked my custom component InfluencerCard, it doesn't do anything.
I wonder it is because that component is in other component, so parent component block clicking on custom component. Or calling onPress function is wrong.
But I tried without parent component, it was working.
What am I missing?
It was my mistake. The problem was not from code or components.
I use Card component from #ui-kitten/components and it implements TouchableOpacity behind the scene. So I don't need to wrap with TouchableOpacity again.So just do
<Card onPress={() => profileClick()}></Card>

When rendering through a helper function I get: "Objects are not valid as a React child" error

I am developing a small project in React Native. I have noticed a weird situation whereby when I render a list through a helper function, I get the eponymous error:
Objects are not valid as a React child
Now this error normally means that I am trying to render an object, which is not the case. I will paste two snippets of code. The first one is how I render the data through a helper function, resulting through an error. The second snippet is how I render the data directly in the render() method and working successfully.
Snippet #1: Rendering through helper function renderUsers() -> does not work
renderUsers = async () => {
return this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
);
};
...
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.renderUsers()}
</ScrollView>
</View>
);
}
Snippet #2: Rendering directly inside render() function -> works OK
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
)}
</ScrollView>
</View>
);
}
What could be the reason?
Your snippet 1 should be like this.
renderUsers = () => {
return this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
);
};
...
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.renderUsers()}
</ScrollView>
</View>
);
}
You need to remove the keyword async
The async function will return Promise Object, which is not supposed to be a React child.
But you no need async function for Array map.
If you want to render something asynchronously try updating state by this.setState and render it accordingly.
You shouldn't return null in render method!
You should render an element like this:
render() {
<View style={{ flex: 1 }}>
<ScrollView>
{
(!this.props.isFetchingUsers && this.props.userList) &&
this.renderUsers()
}
</ScrollView>
</View>
}
then remove the keyword async from renderUsers method.

Categories