Drawer react native map items - reactjs

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.

Related

How to dynamically create Stack.Screen ? - I can't navigate to dynamically created Screens

I have an object that I am trying to loop through to create Screens dynamically. I have a Drawer that contains a list of items that each have an onPress function to navigate to those dynamically created Screens. However, when I press an item from the Drawer, nothing happens. I am wondering if my loop is incorrect, but I can't determine why; if i console.log, I can see all the appropriate info needed to create the right Screen. If I create Screens manually, the onPress does navigate to the appropriate Screen
This is the App.js
const drawerItemsMain = [
{
key: 'Pastry',
title: 'Pastry',
routes: [
{nav: 'MainDrawer', routeName: "LemonBar", title: 'LemonBar'},
],
},
]
const MainDrawerNavigation = () => {
return(
<Drawer.Navigator drawerContent={(props) => <CustomDrawerContent drawerItems={drawerItemsMain} {...props}/>}>
<Drawer.Screen name="Home" component={HomeScreen} />
{
drawerItemsMain.map((item) => {
item.routes.map((route) => {
console.log(route.title)
return (
<Drawer.Screen
key={route.title}
name={route.title}
component={route.routeName}
options={{ title: route.title }}
/>
)
})
})
}
//<Drawer.Screen name="LemonBar" component={LemonBar} /> //this works
</Drawer.Navigator>
)
}
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="MainDrawer" component={MainDrawerNavigation} />
</Stack.Navigator>
</NavigationContainer>
);
}
Thank you in advance for any help
You have forgotten to add a return statement. The following fixes the issue.
<Drawer.Navigator drawerItems={drawerItemsMain}>
<Drawer.Screen name="Home" component={HomeScreen} />
{
drawerItemsMain.map((item) => {
return item.routes.map((route) => {
console.log(route.title)
return (
<Drawer.Screen
key={route.title}
name={route.title}
component={route.routeName}
options={{ title: route.title }}
/>
)
})
})
}
</Drawer.Navigator>
Notice as well that the navigation framework won't create the actual screen component in means of a JSX component. Hence, there must exist a JSX component named LemonBar in your project which is also imported in the file in which your drawer is initialized.
In that sense there is no "dynamic creation of screens in terms of components". If the components are imported and exist in your project, then you can add them to the drawer navigator as a screen using the above snippet.
I think uou have to pass the id or link of appropriate screen for the onPress .
You can definitely create screen dynamically, however you have two array.map functions, one returns screens, and the englobing one returns nothing.
try
{drawerItemsMain.reduce((res, group) => [
...res,
...(group.routes.map((route) => (
<Drawer.Screen
key={route.title}
name={route.title}
component={route.routeName}
options={{ title: route.title }}
/>
))
],
[], // reducer inital state
)}
( Nesting .map in a .map would have returned a an array of array of screens. using .reduce here to have a flat array of screens )

Unexpected behavior of React render props

I've come up with the following code using React Transition Group:
const Transition = ({ elements, selectKey, render: Element, ...props }) => (
<TransitionGroup {...props}>
{elements.map((element) => (
<CSSTransition
key={selectKey(element)}
timeout={1000}
className="transition-slide"
>
<Element {...element} />
</CSSTransition>
))}
</TransitionGroup>
)
The key part here is that Transition component receives the render prop and renders it applying some transitions.
The way I expected for this to work:
<Transition render={(props) => <Toast {...props} />} />
But this code does not work as I expected: the transition of the next element interrupts the transition of the previous one.
However, this code works just fine:
const Element = (props) => <Toast {...props} />
// ...
<Transition render={Element} />
How can I fix this issue without putting the desired render-prop into a separate component?
Codesandbox: Example sandbox. The sandbox presents a non-working option with animation interruption. To get a working version, you need to uncomment lines 16 and 30 in the /Toasts/index.js file
P.S. I can't just use render={Toast} because I need to do ({id}) => <Toast dismiss={() => {deleteToast(id)}} />. I omitted this detail in order to simplify the understanding of the problem.
If you don't want to put the render function into another component, putting it into a useCallback() solved it for me.
const Toasts = () => {
const [toasts, addToast] = useToasts();
const Element = useCallback((props) => <Toast {...props} />, []);
return (
<div>
<button onClick={addToast}>Add toast</button>
<List>
<Transition
elements={toasts}
selectKey={({ id }) => id}
render={Element}
/>
</List>
</div>
);
}
(I don't quite understand the origin of the issue, but it has to do something with the function references.)

React Navigation v5: How to write a navigation route for a custom Navigation header button?

I am trying to add a "Gear" button on headerRight of Navigation header for all screens of my app. I added the button as ScreenOptions of Stack.Navigator inside a NavigationContainer in my App.js so that all Stack.Screens inside this Stack will have this button on header.
Now I need this button press to navigate to another screen (settingsScreen). I cannot add navigation.navigate('settingsScreen') onto the onPress event of the button like I do from the screens because there is no navigation prop available in the App.js file. Here is my code snippet.
const Stack = createStackNavigator();
const myStack = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerRight: () => (
<Button
title='Gears'
onPress={() => {}} // no navigation prop available on this file
/>
),
}}
>
<Stack.Screen
name='Home'
component={HomeScreen}
options={{ title: 'Home' }}
/>
<Stack.Screen
name='Add new expense'
component={AddNewExpense}
/>
<Stack.Screen
name='Settings'
component={SettingsScreen}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
How can this be achieved? Thanks in advance!
screenOptions will receive the navigation prop and the route prop for each screen see here
screenOptions={({ navigation}) => ({
//you can use navigation now
headerRight: () => (
<Button
title='Gears'
onPress={() => navigation.navigate('settingsScreen')}
/>
),
})}

React hooks : 'Cannot read property 'push' of undefined'

I'm trying to redirect my homepage to "/call" page based on a redux state. I can go to that component by typing the url manually but cant do it with a function. I tried "Redirect to", "history.push" but none of them worked for me. I cant solve the problem. Here is my code;
const Phone = ({ hidden, photoOpened, inCall }) => {
const dispatch = useDispatch(getContacts());
let history = useHistory();
useEffect(() => {
if (inCall.inCall) {
history.push('/call')
}
}, [inCall]);
useEffect(() => {
dispatch(getContacts());
}, [])
return (
<div hidden={process.env.NODE_ENV === 'development' ? !hidden : hidden} className={photoOpened ? "phone-container-rotate" : "phone-container"}>
<div className="coque" />
<Suspense fallback={<div className="animated fadeIn pt-1 text-center">Loading...</div>}>
<HashRouter basename="/phone">
<div
className="phone-content"
style={{ backgroundImage: `url(${background})` }}
>
<HeaderBar />
<BannerNotifications />
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
render={props => <route.component {...props} />}
/>
) : null;
})}
</Switch>
</div>
<Route component={BottomPhoneNavigator} />
</HashRouter>
</Suspense>
</div>
);
};
You could try and test for history existence of the history in your effect, also add it to dependency list
useEffect(() => {
if (history && inCall.inCall) {
history.push('/call')
}
}, [inCall, history]);
And important thing, your component using this hook must be within the Router, I see you'\re using HashRouter but as child of component using the hook.
Also if you're stuck to this structure, why wont you try to use Redirect within the Switch? This could work with some smart test so you wont end up in a loop:)
To use history your Phone component should be inside router component

Pass parameter to component 2 levels deep React Native

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

Resources