How to change navigation header button style based on a state property in React Native using React Navigation? - react-native

I'm using react navigation in my RN app and trying to implement a form to submit some information. I'm using a button at the right header and want to style the button with different color to indicate whether the form is legal (e.g., white for a legal form and transparant for having important inputs left blank).
I use this.state.submitDisabled to indicate its legality and define the right header in componentDidMount() and pass the navigation param to the header to render in navigationOptions:
this.props.navigation.setParams({
headerRight: (
<MaterialHeaderButtons>
<Item title="submit" iconName="check"
color={this.state.submitDisabled ? colors.white_disabled : colors.white}
onPress={() => {
if (!this.state.submitDisabled) {
this.postEvent();
}
}}/>
</MaterialHeaderButtons>
),
});
However, the color change statement based on the value of this.state.submitDisabled did not work. I've checked its value, when this.state.submitDisabled is changed, the color of the button does not change. It seems that the color is determined when set as navigation params as above and will not change since then.
How can I achieve the effect of what I described?

when ever you change value of state also change navigation param too.see example
export class Example extends Component {
static navigationOptions = ({ navigation }) => {
const showModal = get(navigation, 'state.params.showModal');
return {
headerTitle: <Header
showModal={showModal}
backIcon />,
headerStyle: HeaderStyle,
headerLeft: null,
};
};
constructor(props) {
super(props);
this.state = {
showModal: false,
};
}
componentDidMount = () => {
this.props.navigation.setParams({
showModal: this.state.showModal,
});
}
handleModal=(value)=>{
this.setState({showModal:value});
this.props.navigation.setParams({
showModal: this.state.showModal,
});
}
render() {
return null;
}
}

In your implementation this.state.submitDisabled is not bound to the screen. Try the following:
static navigationOptions = ({ navigation }) => {
headerRight: (
<MaterialHeaderButtons>
<Item
title="submit"
iconName="check"
color={navigation.getParam('submitDisabled') ? colors.white_disabled : colors.white}
onPress={navigation.getParam('handlePress')}
/>
</MaterialHeaderButtons>
),
})
componentWillMount() {
this.props.navigation.setParams({
submitDisabled: this.state.submitDisabled,
handlePress: () => {
if (!this.state.submitDisabled) {
this.postEvent();
}
}
});
}

Related

make dynamic tab on createMaterialTopTabNavigator of react-navigation

I'm making the top tab navigator using createMaterialTopTabNavigator of react-navigation. The problem I faced is that the tabs have to decide by the response data of API request. For example, I call an API to request the football team list, receive the list and then set the tabs of the MaterialTopTabNvigator.
I already make the label of the navigator using the component like following :
class TabBarLabel extends React.Component {
constructor(props) {
super(props);
}
render() {
return this.props.focused ? (
<View style={lstyles.container_focused}>
<Text style={lstyles.label_focused}>{this.props.name}</Text>
</View>
) : (
<View style={lstyles.container_blur}>
<Text style={lstyles.label_blur}>{this.props.name}</Text>
</View>
);
}
}
const FootballTeamNavigator = createMaterialTopTabNavigator(
{
teamA : {
screen: AScreenComponent,
navigationOptions: () => {
return {
title: 'teamA',
tabBarLabel({focused}) {
return <TabBarLabel focused={focused} name="teamA" />;
}
};
}
}
},
{
initialRouteName: teamA,
swipeEnabled: false,
timingConfig: {
duration: 1000,
},
tabBarOptions: {
scrollEnabled: true,
...styles,
},
},
);
What I want to do is like :
let teamList = {};
apiForGetFootballTeamList().then(response => {
for (const team of response.data.list) {
tempList[team.name] = team;
}
});
createMaterialTopTabNavigator(
{
...teamList
},
{
initialRouteName: ...,
...
}
);
But I don't know how can I update the tabs using data, like component. (component has state and if we update the state, the component is updated)
Is there any way for it?
Current versions of React Navigation don't support dynamic configuration. You need to use React navigation 5 for that https://blog.expo.io/announcing-react-navigation-5-0-bd9e5d45569e
Got to a couple of different threads trying to do this (this one among others). Did not manage to find a solution, but in the end it ended up being rather easy.
<Tab.Navigator>
{villages.map(village => (
<Tab.Screen
key={village.villageId}
name={village.name}
component={ResourceFieldScreen}
initialParams={{ village: village }}
/>
))}
</Tab.Navigator>
At the top, before the function itself I have:
const Tab = createMaterialTopTabNavigator();
In this case screens are created based on how many counts of village there are in villages. Then a prop of village is sent to the component that is supposed to render it all ResourceFieldScreen. Which makes it possible to show whatever is included in villages, on that screen.
Code for that part:
function ResourceFieldScreen({ route }) {
const village = route.params.village;

How can i hide and show stack navigator header in react-native?

How can I hide and then show header(stack navigator) by pressing a button ?
static navigationOptions = ({ navigation }) => {
return {
header : null
}
}
This code set header to null and hide header but i can't show it again.
You can try something like this
static navigationOptions = {
headerVisible: this.state.headerVisible,
};
And In the constructor Initialise the state by
this.state = {headerVisible: true}
And on the buttonPress You can change the state by
<Button onPress={() => this.setState({headerVisible: !this.state.headerVisible})} />
Do you could try this?
this.state={
header: null
}
static navigationOptions = {
header: this.state.header,
};
...
headerfunc(){
if(this.state.header === null){
this.setSate({
header: ""
});
}else{
this.setSate({
header: null
});
}
}
...
<Button onPress={() => this.headerfunc()} />
You can add a custom function to handle the header visibility.
Create a handler function in your screen component,
toggleHeader=()=>{
let currentVal = this.props.navigation.getParam('isHeaderVisible', true);
this.props.navigation.setParams({ isHeaderVisible: !currentVal });
}
Add this handler function to your Button
render(){
...
<Button onPress={() => this.toggleHeader()} />
...
}
Finally, add static navigationOptions in your screen,
static navigationOptions = ({navigation}) => {
let headerVisible = navigation.getParam('isHeaderVisible',true);
return {
headerVisible
}
}
Thanks for every one. headerVisible property doesn't work.
There is another property called headerMode, it just works in stack navigator's config and we can't change it in our screen :
const StackNaviagtor = createStackNavigator({
showScreen: {
screen: MyScreen
}
}, {
headerMode: 'none'
})
only header property in navigationOption work and we can change it in our screen
the solution is :
import { Header } from "react-navigation";
static navigationOptions = ({ navigation }) => {
return {
header: navigation.getParam('isFullscreen') ? null : (headerProps) => <Header {...headerProps} />
}
and then:
render() {
let isFullscreen = this.props.navigation.getParam('isFullscreen');
return (
<Button title='Full Screen' onPress={() => { this.props.navigation.setParams({ isFullscreen: !isFullscreen }) }} />
)

how to a call static function (eg: static navigationOptions) from another function in react native

how can i call a static function (static navigationOptions) from any other function in react native?
It fails when using the this keyword, but is it possible to render static navigationOptions again by calling it?
If you want to change navigation options dynamically, try like this
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('otherParam', 'A Nested Details Screen'),
};
};
this.props.navigation.setParams({otherParam: 'Updated!'})
Another method
static navigationOptions = ({ navigation }) => {
const {state} = navigation;
return {
title: `${state.params.title}`,
};
};
ChangeThisTitle = (titleText) => {
const {setParams} = this.props.navigation;
setParams({ title: titleText })
}
call this.ChangeThisTitle('your title') wherever you want
They only way to achieve it is using navigation params. So set your headerleft property flag or value using setParams. That will solve the issue. Below mentioned code should be used in your class.
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
let buttonView =
<TouchableOpacity style={navItemStyle} activeOpacity={0.7} onPress={() => { params.logoutClick() }}>
<Text style={navItemTxt}> Logout</Text>
</TouchableOpacity >
return {
title: 'Home',
headerLeft: params.showHeaderLeft && buttonView
};
};
componentDidMount() {
this.props.navigation.setParams({
showHeaderLeft: this.props.headerFlag,
//headerFlag used above is your value and showHeaderLeft is the name of the param
});
}

How to update the header while the component is still rendered using React Navigation?

I'm writing a React Native app and I'm using React Navigation (V2) with it. I want to update the navigationOptions and add a new button, after my component has updated. Here is the code with which I tried it:
static navigationOptions = ({ navigation }) => {
const options = {
headerTitle: SCREEN_TEXT_MENU_HEADER,
headerStyle: {
borderBottomWidth: 0,
marginBottom: -5
}
};
if (navigation.getParam("drawer", true)) {
options["headerLeft"] = (
<HeaderIconButton
onClick={() => {
navigation.openDrawer();
}}
icon={require("../../assets/icons/burgerMenu.png")}
/>
);
}
if (navigation.getParam("renderBillButton", false)) {
options["headerRight"] = (
<HeaderIconButton
onClick={() => {
navigation.navigate("BillScreen");
}}
type="primary"
icon={require("../../assets/icons/euro.png")}
/>
);
}
return options;
};
componentDidUpdate = prevProps => {
const { navigation, orders } = this.props;
if (prevProps.orders.length !== orders.length) {
navigation.setParams({
renderBillButton: orders.length > 0
});
}
};
The problem with this approach is, that the navigationOptions do not get reset after componentDidUpdate(). How can I dynamically adjust the header with React Navigation?
You can use this.props.navigation.setParams() function to update the navigation state params.
Reference: https://reactnavigation.org/docs/en/headers.html#updating-navigationoptions-with-setparams
Okay here is what went wrong: I also had to call the same code within componentDidMount(), otherwise it would not affect the page upon loading. So in addition to the code of my question I added:
componentDidMount = () => {
const { navigation, order } = this.props;
navigation.setParams({
renderBillButton: orders.length > 0
});
}

How to update DrawerLabel text (via navigationOptions) on DrawerOpen/Close event

I have define drawerLabel in navigationOptions as follows :
export default class Something extends React.Component {
static navigationOptions = {
drawerIcon: () => (
<Image source={drawerImage} style={styles.imageStyle} />
),
drawerLabel: ()=>{
if(~some condition~){
return 'New Label'
}else if(~some other condition~){
return 'Another Label'
}else{
return 'label'
}
},
};
This updates the label perfectly when any drawerItem is pressed in the drawer.
(apparently opening ANY drawer item updates the navigationOptions of all routes)
But I want this drawerLabel to be updated on drawer.Open() call as well.
or when left-header is pressed on any screen (triggers drawer.Open() )
So, how can I achieve this behaviour on DrawerOpen/Close ?
I am able to achieve this on pressing any drawerItem
Pass navigation prop to your navigationOptions.
static navigationOptions = ({ navigation }) => ({
drawerLabel: ()=>{ // maybe you also need to pass navigation to this function through the parentheses (navigation)
if(~some condition~){
return navigation.state.params.labels.first
}else if(~some other condition~){
return navigation.state.params.labels.second
}else{
return navigation.state.params.labels.third
}
},
});
Obviously, you need to create the labels:
navigation.setParams({ labels: { first: 'labelOne', second: 'labelTwo', third: 'labelThree' }})
before you can access them.
If you don't understand how to add the labels to the params - don't hesitate asking.
You can also use screenProps, if you have static labels. In this case you will need to define these props in your root-navigator. After that you will be able to access them in navigationOptions if you pass them alongside with navigation:
static navigationOptions = ({ navigation, screenProps }) => ({
I managed to achieve the desired output as follows (im afraid it might cause performance issues in the long run) :
I created a custom component and forced it to re-render every second using :
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000); }
And used that component in the navigationOption's drawerLabel :
drawerLabel: ()=> {
return <CustomComponent />
}
Now it does not need a click event on either any drawerItem or in drawerToggle call. (label gets updated even if the drawer is left open and no click is fired at all)
Following is the CustomComponent's render code :
if(~some condition~){
return <Text> New Label </Text>
}else if(~some other condition~){
return <Text> Another Label </Text>
}else{
return <Text>label</Text>
}
Now the if/else condition gets checked every second and a Text component is rendered as a label accordingly.
Even though I managed to achieve what I required, I would like to know if this affects the performance of the application?
(if so should I optimize this somehow? ... async maybe)