How to Create Drawer menu without Navigation? - react-native

I am looking to create a drawer, similar to a drawer navigator, but without the routes/navigation requirement.
I plan on placing some other components there that update a query. Any recommendations? Specifically the drawer would be used to display picklists, sliders, and date range components that would update the state and variables used in updating markers rendered on a map shown on the home page.

With Redux
You can use the contentComponent of the createDrawerNavigator to create your own custom drawer and bind it to redux-store.
By dispatching the actions with relevant queries you can update the variables as they are passed from the store to your Component.
Without Redux
You can either create a CustomDrawer component with similar animation and render it in your Component or use this react-native-drawer.
import Drawer from 'react-native-drawer'
class Application extends Component {
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render () {
return (
<Drawer
ref={(ref) => this._drawer = ref}
content={<DrawerContentComponent {...// Your Updater props}/>}
>
<Component />
</Drawer>
)
}
})

Related

FlatList `onEndReached` callback is not called when swipe to the end of the list

The FlatList component has a onEndReached prop. I am implementing a simple feature that when user swipe the screen to the end of the list, the app sends a new request to get more items to the list from backend. The following is what I tried:
I firstly created a custom component MyCustomList which is just a wrapper of FlatList. There are two props items and getMore.
const MyCustomList = ({items, getMore}) => {
...
return (
<FlatList
keyExtractor={keyExtractor}
data={items}
renderItem={renderItem}
onEndReached={getMore}
onEndReachedThreshold={0}
ListFooterComponent={myFooter}
/>
);
...
export default MyCustomList;
}
items are the data to be shown on the FlatList,
getMore is the function that send request to backend to get more items. It is supposed to be called when swiped to the end of the list.
In my screen component, I use above MyCustomList component as below:
import React, {useState} from 'react';
import MyCustomList from '../components/MyCustomList';
import {useQuery} from 'react-query';
const MyScreen = ({navigation}) => {
const [page, setPage] = useState(1);
// when screen is rendered, get the data from backend
const {data, status} = useQuery('my-items', httpClient.fetchItems);
// this is the function passed to MyCustomList component
const getMoreItems = () => {
// I expected to see this log every time when user swiped to the end of the list
console.log('get next 10 items');
setPage(page + 1);
};
return (
<View>
...
<MyCustomList items={data} getMore={getMoreItems} />
</View>);
};
As you can see, in my screen component I pass the getMoreItems function to MyCustomList.
When I run my app, I only see that log message I put inside getMoreItems function once when the screen is first time shown. It's weird to me that I haven't swiped to the end of the list at that point. After that, every time when I swipe to the end of the list, the getMoreItems is not triggered (I don't see that log message). Why is that?
P.S. when swiping to the bottom of the list, my screen is not re-rendered now because the getMoreItems is not triggered the setPage(page+1) is not called.

access `headerRight` inside a screen that is hosted by stack navigator

I have created a stack navigator:
import {createStackNavigator} from '#react-navigation/stack';
const TheStack = createStackNavigator();
Then, This is my navigator, it claimed component={LandingScreen}:
<TheStack.Navigator ...>
<TheStack.Screen
name="LandingScreen"
component={LandingScreen}
options={{
title: '',
headerLeft: null,
headerRight: () => (
<MyHeaderRightComponent />
),
}}
/>
<TheStack.Navigator>
As you can see above in options of the screen, there is headerRight, I have declared using MyHeaderRightComponent as headerRight so that it is shown on the right side of the header on screen.
Here is my LandingScreen.js :
import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
const LandingScreen = ({navigation}) => {
// How can I access the `headerRight` component I have set above from here?
...
}
My question is how can I access the headerRight inside my LandingScreen.js? I know I can update or reset the headerRight by:
navigation.setOptions({headerRight:() => <NewHeaderRightComponent/>})
But now what I need is to access the previous already set component, not setting a new one. How to do that?
Edits to the answer as per the request received in comments. The answer is the same. This is just further demonstration on how to use it.
// The screen component where you want to pass the state.
const Screen = (props) => {
const [color, setColor] = useState("#CCCCCC");
const { navigation } = props //This is important or else UseEffect will be called each time any of the props change
useEffect(() => {
navigation.setParams({ color: color }); // Where its being passed.
}, [color, navigation]);
return (
<>
<Button onPress={() => setColor("#800000")} /> // Change the color state to Maroon
<Button onPress={() => setColor("#FED700")} /> // Change the color state to Gold
</>
)
}
Your Header Component:
const MyHeaderComponent = (props) {
return(
<View style={{ backgroundColor: props.bgColor }} />
)
}
Then you can retrieve this bit in headerRight. Like this:
headerRight:() => <MyHeaderComponent bgColor={route.params.color} />
Note: This method is valid for React Navigation v5. Version 4 has a getParams() function to retrieve the params, but it was dropped in Version 5.
Original Answer
You can create a useState hook in the screen and pass its value into your header component. So, when the header component updates the state, it can be accessed from within the screen where you have defined the state.
you can use setParams() function to set the params you want to use in the header component. Then, use route.params.nameofyourprop to get them in the headerComponent, where you can consume it.
This is to pass params from outside the header to inside of it.
headerRight:() => <MyHeaderRightComponent propname={route.params.propvalue} />
This to to set the Params from outside your header which you can access inside the headerRight component.
const [values, setValue] = useState()
navigation.setParams({propname: value})
This way you can pass state between the header and the screen.
You can also pass the setValue function of the useState in this manner, but it will throw a warning because functions are objects in Javascript and thus its not possible to index them... or something on those lines.

How do I change the content of a react native drawer like on a regular screen?

Suppose you have a drawer navigator created through
const GalioApp = createDrawerNavigator(screens, options);
How can I change the drawer content like in a regular screen? For example change what's written on top to something else, or change a navigator button content for example to change the language of it
You can create custom drawer component and set drawerWidth to width of the screen which will open drawer in fullscreen width. Then you can render your custom view on drawer with contentComponent prop of drawer navigator. For example :
import { Dimensions } from 'react-native';
const { width } = Dimensions.get('screen');
const CustomFullScreenDrawer = (props) => {
return (
// make your custom view here
)
}
const GalioApp = createDrawerNavigator(screens, {
drawerWidth: width,
contentComponent: (props) => <CustomFullScreenDrawer {...props} />
});
Here is the full list of props which helps you to create better custom drawer.

Is it possible to render two headers on one screen using React Navigation?

I'm writing an iPad / Android Tablet app with React Native and I'm using React Navigation for the navigation.
The mock up I received has a screen which is split in half (in landscape mode) with two different headers. Is is possible to achieve this with react navigation?
What I've tried is setting header: null in the navigation options for the screen, and then rendering two components on that split screen, each of the components has React Navigation's Header component as the first child. This somehow does not work (the header does not get rendered).
Like you can see in the source of Header it does not render if the header option is null:
_renderHeader(props) {
const { options } = props.scene.descriptor;
if (options.header === null) {
return null;
}
...
}
So using the Header component manually will not work.
You could just create your own Header components with a title and a "back" touchable. If a designer gave you a mock up I assume the Header must be styled very differently from original Header component anyway :)
Your component could look like this:
export default class MyCustomHeader extends Component {
handlePress = () => {
const { navigation } = this.props;
navigation.goBack(); // or .navigate() to whatever you want
};
render() {
return (
<View>
<TouchableHighlight onPress={this.handlePress}>
<some-icon>
</TouchableHighlight>
<Text>My title</Text>
</View>
);
}
}
With proper styling of course :)

can't navigate to different screen using react-native-drawer with react-native-router-flux

I'm using react-native-router-flux for the Navigation system and using react-native-drawer for the sidebar. If an user clicks on menu item which is in the sidebar, I want to redirect user to different screen and close the Drawer. I'm using the following code snippets.
Actions.refresh({key: 'drawer', open: false}) to Close the Drawer
Actions.pageTwo.call() to Open the Second Page
Both are working without any problem if I'm using it in separate functions. But, If I trigger both the snippets from the same function. It is not working.
Thanks in Advance,
I have this scenario working for me with both libraries. At a high level I create a custom component to render the content of react-native-drawer which I pass a function to responsible for closing the drawer. Then when I press one of the drawer items I fire both a react-native-router-flux navigation action (in my case a PUSH) and I call the function passed in to close the drawer.
Here is what defining my drawer looks like. Remember DrawerNavigationConten is ultimately just a ListView or whatever implementation you prefer to render the drawer content.
class RootComponent extends Component {
...
closeDrawer() {
this.drawerRef.closeDrawer();
}
render() {
const drawerNavigationContent = (
<DrawerNavigationContent
closeDrawer={this.closeDrawer.bind(this)}
/>
);
return <Drawer
ref={(ref) => { this.drawer = ref; }}
content={drawerNavigationContent}
...
>
...here I define my react-native-router-flux scenes...
</Drawer>
);
}
...
}
Here are the important items in DrawerNavigationContent
import { Actions } from 'react-native-router-flux';
class DrawerNavigationContent extends Component {
...
navigate(location) {
Actions[location]();
this.props.closeDrawer();
}
...
render() {
...
<TouchableOpacity onPress={this.navigate(...some dynamic scene key...)}>
...
</TouchableOpacity>
...
}
}
I've worked with react-native-router-flux and had some issues with the drawer as well. If both scenes are children of the Drawer like so
<Scene key="navigationDrawer" component={NavigationDrawer}>
<Scene key="pageOne" component={PageOne}/>
<Scene key="pageTwo" component={PageTwo}/>
</Scene>
you can use Actions.pageTwo({key: 'navigationDrawer', open: false}) which should close the drawer upon navigation.
Otherwise you could use Actions.refresh({key: 'navigationDrawer', open: false) on PageTwo's componentWillMount or componentDidMount methods.