Pass parameter to component 2 levels deep React Native - reactjs

Im trying to pass parameters through to a component two levels deep.
I have 2 screens (MainScreen & UserProfileScreen) with a flat list on both screens, both flat lists use the same component EventCard in its renderItem. EventCard is made up of 3 three nested components EventCardHeader,EventCardBody & EventCardFooter. How do I pass certain arguements only from the UserProfileScreen? I have posted code below to give a better understanding of what I have.
MainScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
/>
openEditEvent = (event) => {
this.props.navigation.navigate('EventFormScreen', {
event: event,
eventKey: this.state.eventKey,
editMode: true,
});
};
EventCard
export default class EventCard extends Component {
render() {
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent={() => this.openEditEvent()}
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
};
EvenCardHeader
export default class EventCardHeader extends Component {
render() {
return (
<CardSection style={styles.eventCardHeader}>
<Thumbnail small
style={styles.eventOrganiserImage}
source={{uri: this.props.eventOrganiserImage}}/>
<Text style={styles.eventPromoterName}>{this.props.eventVenue}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() => this.props.openEditEvent()}>
Edit
</Button>
}
{!!this.props.openDeleteEventAlert &&
<Button onPress={() => this.props.openDeleteEventAlert()}>
Delete
</Button>
}
</CardSection>
);
}
}
I can see that because I have hardcoded the this.openEditEvent() function into my EventCard component that what's causing me the problem, because then the if statement in EventCardHeader that checks if the this.openEditEvent() exists always evaluates to true. Would someone be able to help show me the right way to do this? Thanks in advance for any help.
UPDATE:
Added in openEditEvent

Where is openEditEvent() declared? It should be in the parent component and passed as props to whatever children you need it in. You can continue to pass it as props from children to children.
EDIT:
Ok so you can pass openEditEvent as props like so:
<EventCard
openEditEvent = this.openEditEvent
openEventDetail={() => this.openEventDetail(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
That function can be available in EventCard, and can then be passed AGAIN as props to another child component:
render() {
var openEditEvent = this.props.openEditEvent;
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent = openEditEvent
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}

Related

Drawer react native map items

the CustomDrawerContentt component receives the ...props from the parent component.
the parent component is composed of a Drawer.Navigator and a Drawer.Screen, the props that are sent to the child component are the screen of each Drawer.Screen.
what I want to do is instead of passing a ... props from the parent component, I want to directly pass these screens to the CustomDrawerContentt component. ie replace ... props with myScreen.
myScreens:
views: {
Home: AdminView,
CrudCategories: CrudCategories,
CrudItems: CrudItems,
ListItems: ListItems
},
CustomDrawerContentt Component: (child component)
function CustomDrawerContentt(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem
label="Close drawer"
onPress={() => props.navigation.dispatch(DrawerActions.closeDrawer())}
/>
<DrawerItem
label="Logged"
onPress={() => props.navigation.dispatch(DrawerActions.toggleDrawer())}
/>
</DrawerContentScrollView>
)
}
App return:
return (
userRoutes
?
<Drawer.Navigator
drawerPosition='right'
drawerContent={(props) => <CustomDrawerContentt {...props} linksSidebar={userRoutes.sidebar}
/>}>
<Drawer.Screen name="Inicio" component={RoutesModals} />
</Drawer.Navigator>
:
<LoadingScreens />
);
I have tried it before and it has worked for me, but now that I come across ... props the console gives me an error "TypeError: Cannot read property 'routes' of undefined"
for example this component, where userRoutes.views has the same structure mentioned above (myScreens) and it has worked perfectly for me.
const RoutesViews = () => {
return (
<ViewsStack.Navigator initialRouteName="Home">
{Object.entries({
...(userRoutes.views),
}).map(([name, component, index]) => (
<ViewsStack.Screen key={name} name={name} component={component} options={name == 'Home' ? { headerShown: false } : { headerShown: true }} />
))}
</ViewsStack.Navigator>
)
}
I would like to know how I do it instead of passing <DrawerItemList {...props} /> can I pass <DrawerItemList {myScreens} />
You can use Redux to store whatever you want and access it in Child Component. It will be easy to handle.

Handle children.props

so, in ParentComponent I have
<Component dropDownContent={<DropDownContent content={array} onSelect={handleSelect} />} />
my DropDownContent looks something like this
return (<ul>
{content.map((item) =>
{ return <li><button onClick={()=> onSelect(array.id)}>{array.name}</button></li>}
)}
</ul>)
Can I some how do something with the onSelect inside Component even if I add DropDownContent as a prop to Component?
Thanks :)
What I understand from your question is that you want to pass a function from the parent component to the child component. And when a local function inside child component is clicked, you want to call that passed function. if yes, then this is your solution:
Note: I do not know exactly what code you wrote and what your component consists of. So I will give you the answer by giving a simple example to fully understand the solution.
In your parent component:
export const ParentComponent = props => {
const handleSelect = () => console.log(`do something here`);
return (
<Component
dropDownContent={
<DropDownContent
content={array}
onSelect={() => handleSelect()}
/>}
/>
)
}
And in your child component you need to receive the passed function like below:
export const ChildComponent = props => {
const handlePassedFunction = () => props.onSelect?.();
return (<ul>
{content.map((item) => {
return <li>
<button onClick={() => handlePassedFunction(array.id)}>{array.name}</button>
</li>
}
)}
</ul>)
}

How to architect a single modal component accessible by multiple components in React Native?

I'm new to react and I'm having trouble getting my head around the architecture of my app. Here's what I've got:
I use fetch() to get an array of records (records) from the backend. I use a couple of custom components to handle outputting the data. At the end of the tree is a series of touchable components which I need to open a single modal component on press. Here's a simple version of the code:
./app.js
<ScrollView>
{records.map((record, index) => {
return <Post key={index} post={record} />
})}
</ScrollView>
./src/components.js
function Post(props) {
return (
<Child info={props.post} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => ...open modal...}>{props.info}</TouchableHighlight>
)
}
So I can't put my <Modal> in either of the components in components.js, as then I end up with one modal for each record in the database. So I figure I need to put it at the end of the <ScrollView> in app.js, but then how do I open and close it from the <Child> component? I've read that it's possible to pass info back up the chain to a parent component using a callback function, but I'm unsure of how to do that while also passing props down that contain the information of each record.
You can try this logic:
const [modalVisible, setModalVisible] = useState(false);
const handleVisibility = () => {
setModalVisible(!modalVisible)
}
return(
<View>
<YourWrappingModalComponent visible={modalVisible} />
<ScrollView>
{records.map((record, index) => {
return <Post
key={index}
post={record}
handleVisibility={handleVisibility} />
})}
</ScrollView>
</View>
)
To open modal from Child you do:
function Post(props) {
return (
<Child info={props.post} handleParentModal={props. handleVisibility} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => props.handleParentModal()}>{props.info}</TouchableHighlight>
)
}
Using a component like YourWrappingModalComponent where you define your modal is very useful if you use your modal in many places.

semantic-ui-react List onClick declaration

i'm trying to create a list of documents dynamically with semantic-ui-react. I'd like to get the document title back when the list item is clicked. According to the documentation:
https://react.semantic-ui.com/elements/list
there is an onItemClick prop for this reason, however when im using it i get a warning when it's rendered:
Warning: Failed prop type: Prop onItemClick in List conflicts with props: children. They cannot be defined together, choose one or the other.
Also clicking on the list item does nothing (atm i just want to log the doc title to the console). Here is the code:
handleListItemClick(event, data) {
console.log("list item clicked: " + data.value);
}
buildResultsContainer() {
return this.props.listOfResults.map((document,index) =>
{
return (
<List.Item
as='a'
key={index}>
<Icon name='file' />
<List.Content>
<List.Header>{document.properties.title}</List.Header>
<List.Description>
{document.properties.description}
</List.Description>
</List.Content>
</List.Item>
);
}
);
}
render() {
return (
<div>
<List onItemClick={this.handleListItemClick}>
{this.buildResultsContainer()}
</List>
</div>
)
}
Can you please tell me how to use properly the onItemClick prop for the List component?
Less important, do you have any tip how to refactor the list rendering? Just wanted to keep the render function short and clean, but this function call looks a bit overkill....
Thanks a lot!
I think maybe the intent when using onItemClick is that you would use the items prop on List since then you wouldn't have any children e.g.
render() {
const items = this.props.listOfResults.map(document => {
return {
icon: 'file',
content: document.properties.title,
description: document.properties.description,
onClick: e => console.log(document.title)
}
});
return <List items={items} />
}
If you had your listOfResults prop in the above format, you wouldn't even need to do this map and your render function would be super tight:
render() {
return <List items={this.props.listOfResults} />;
}
Alternately, List.Item takes an onClick prop that you could define in your buildResultsContainer() function. Because each onClick function is unique based on the current document object, you will need to use an anonymous function to call your handleClick function as follows:
<List.Item
onClick={() => this.handleClick(document.title)}
...etc
/>
You would then have:
handleClick = docTitle => {
console.log(docTitle);
};
If what you wanted was obtainable from event.target, you could just pass the reference of handleClick to the onClick i.e.
handleClick = event => {
console.log(e.target.innerText);
};
<List.Item
onClick={this.handleClick}
/>

Trigger onProperty of component

I have tried to trigger (refer, call, correct terminology?) an onScale from the property of this component from the react-native-photo-view API:
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
onScale={() => console.log('onScale called')}
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>
How could i "trigger" this onScale={} property from lets say a <Button onClick={triggerOnScale()} /> to zoom with the scale factor? Any direct code example or pointers to techniques/methods that could help would be greatly appreciated!
If onScale is a method of your PhotoView component then you can set up a reference to call it:
handleOnClick = () => this.photoView.onScale()
//just use the handleOnClick wherever you want your click and it will call onScale also
<PhotoView
source={{ uri: }}
onLoad={() => console.log('onLoad called')}
onTap={(event) =>
console.log(`onTap called: ${event.nativeEvent.x}${event.nativeEvent.y}`)}
**ref={(photoView) => this.photoView = photoView}**
minimumZoomScale={1}
maximumZoomScale={3}
scale={0.5}
resizeMode={'contain'}
androidScaleType={'fitXY'}
style={styles.photo}
/>
If you want your onScale handler and your button's onClick handler to fire the same function, then you can just bind a function to the containing component and then pass the function's reference to the Button and PhotoView respectively.
class YourComponent extends Component {
constructor(props){
super(props);
this.someFunction = this.someFunction.bind(this);
}
someFunction() {
console.log('Inside some Function.);
}
render() {
return(
<div>
<PhotoView onScale={this.someFunction} />
<Button onClick={this.someFunction} />
</div>
);
}
}

Resources