How to make react navigation header dynamic? - javascript

In my react native app, I am trying to implement a message box in the header which when clicked shows a screen with the users messages. I am trying to make the message icon shown in the header vary depending on whether the user has unread messages or not.
In my AppNavigator file, I have tried using a function which stores whether or not there are unread messages and then returns what should be displayed in the header accordingly.
navigationOptions: ({ navigation, goBack }) => ({
title: "Title",
headerRight: () => {
// unread_messages = check if user has any unread messages
}).then((unread_messages) => (
<View style={{flexDirection: "row"}}>
<TouchableOpacity
onPress={() => {
navigation.navigate("Messages");
}}
>
<View style={{ flexDirection: "row" }}>
{ unread_messages ? (
<Ionicons name="md-mail-unread" size={20}/>
) : (
<Ionicons name="md-mail" size={20}/>
)
</View>
</TouchableOpacity>
</View>
))
}
})
Doing this results, in nothing except the title being rendered in the header. Is it possible to query data in navigationOptions and then render depending on that? Thanks in advance

In your case, My suggestion is you should use redux store to store the read or unread status of your user's message.
Data query should be put on your screen, then update store's state, the icon will change.

Related

React Native FlatList refreshing not working

i got an problem with the refreshing on pull function. The FlatList renders fine, but pull to refresh is not working. This is my current sourcecode:
return (
<View style={GlobalStyles.flex1}>
<FlatList
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={() => {
console.log("onRefresh loadVocable");
loadVocables();
}}
/>
}
data={vocables}
keyExtractor={vocable => vocable.id}
onEndReached={() => {
if (!isRefreshing && !endReached) {
loadVocables();
}
}}
renderItem={vocable => (
<TouchableOpacity
onPress={() => {
props.navigation.navigate({ routeName: "editVocable", params: { vocable: vocable.item } });
}}
onLongPress={() => {
handleLongPress(vocable.item.id);
}}>
<Card style={styles.cardStyle}>
<View style={styles.part1}>
<Text style={styles.vocableText}>{vocable.item.wordENG}</Text>
<Text style={styles.vocableText}>{vocable.item.wordDE}</Text>
</View>
<View style={styles.part2}>
<Ionicons name={vocable.item.known ? "md-checkmark-circle" : "md-close-circle"} size={23} color={vocable.item.known ? Colors.success : Colors.danger} />
</View>
</Card>
</TouchableOpacity>
)}
/>
</View>
);
In the official docs is an example that says contentContainerStyle needs to be flex: 1 to know the height, that makes sence to me, so when i set contentContainerStyle with flex 1, refresh on pull works fine, but then i can't scroll anymore in the Flatlist and everthing get very tight, so the style also change then. Does anyone know why this happen?
The first picture is with "contentContainerStyle={{flex: 1}}" and the last one is without contentContainerStyle.
The answer was so easy, I compared a new project (there worked my code) to the one where the problem was and after 5 days I found the little error:
My import was wrong!
I imported FlatList like this:
import { FlatList } from "react-native-gesture-handler";
But it needs to get imported from React-Native so like this:
import { FlatList } from "react-native";
Thanks to #The1993, without the hint to compare the projects, maybe I would stuck forever on this error :D In the future I will compare working files to find any error!
contentContainerStyle is used to style inner content e.g items alignments, padding, etc
style is used to align its height and relations
You can replace style={{flex: 1}} instead of contentContainerStyle or wrap the parent element with flex: 1

How to pass Button component's title into a function in React Native

I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>

How to call multiple screens on one page in React Native

I have made a Home page in which there are three buttons in the header (like a tab navigator) I want something like on clicking each button a screen appears beneath the header, as shown in the image below:
Here's what I have tried:
constructor(props) {
super(props);
this.state = {
initialstate: 0, //Setting initial state for screens
};
}
render(){
return(
<View style={styles.container}>
<TouchableOpacity onPress={() => this.setState({ initialstate: 0})}>
<Image source={require('../../assets/add.png')}
resizeMode="contain"/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.setState({ cardstate: 1})}>
<Image source={require('../../assets/request.png')}
resizeMode="contain"/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.setState({ cardstate: 2})}>
<Image source={require('../../assets/send.png')}
resizeMode="contain"/>
</TouchableOpacity>
{this.state.initialstate == 0 ? ( <RequestComp/> ) : ( <TopUpComp/> ) }
//Over Here when I use the Third Screen like " : <SendComp/> " it gives me JSX error says "EXPECTED }"
</View>
Ciao, what you need seems more like a popup that appears from the bottom of the screen. I use react-native-popup-ui Toast component to do that.
Something like:
import { Root, Toast } from 'popup-ui'
...
<Root>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TouchableOpacity
onPress={() =>
Toast.show({
title: 'User created',
text: 'Your user was successfully created, use the app now.',
color: '#2ecc71'
})
}
>
<Text>Call Toast</Text>
</TouchableOpacity>
</View>
</Root>
And the result is:
Note: I have android and the Taost is overlapped by android navigation bar but on iOS you should see all the Toast component
you are using a ternary operator ( initial ? true : false ) which can work only for two components in your case.

React Native : TabBarIOS and Navigation

I have simple app, with 3 tabs. Tabs are defined by TabBarIOS in index.ios. I’m not using Navigator nor NavigatorIOS. In each TabBarItem , I just put the component name in tags. Like this :
return(
<TabBarIOS
selectedTab={this.state.selectedTab}
//unselectedTintColor="yellow"
//tintColor="white"
//barTintColor="darkslateblue"
>
<TabBarIOS.Item
icon={{uri: 'courses.png', scale: 5}}
title="Courses"
badge={undefined}
selected={this.state.selectedTab === 'courses'}
onPress={() => {
this.setState({
selectedTab: 'courses',
});
}}>
<Courses />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'register.png', scale: 5}}
title="Register"
badge={undefined}
selected={this.state.selectedTab === 'register'}
onPress={() => {
this.setState({
selectedTab: 'register',
});
}}>
<NavigatorIOS
//style={styles.nav}
initialRoute={{
title : 'Register',
component: Register
}}
/>
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'profile.png', scale: 5}}
title="Profile"
badge={undefined}
selected={this.state.selectedTab === 'profile'}
onPress={() => {
this.setState({
selectedTab: 'profile',
});
}}>
<Profile />
</TabBarIOS.Item>
</TabBarIOS>
);
If you see in code, in first and third tab, i'm showing content of the item by puting component name in tags like
<Courses />
But for second item, i tried using navigatorios to show component, but it shows only a blank page with title. it doesn't show the content of component. i see lots of examples working like that, but it didn't work for me. maybe because i'm not using navigator or navigaatorios for index.ios , because in most examples i see, they put navigatorios for index and set initialroute. i tried it, but didn't work for me.
Everything works fine till here. In Courses page, I have a list view with items under it (actually items with header, which are collapsible) , when user clicks on each item , I need to redirect user to a page to show that course’s details. (I should pass some arguments also).
render(){
return (
<View style={{
flex: 1
}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
style={styles.listView}
/>
</View>
);
}
renderRow(data) {
var header = (
<View>
<View style={styles.rowContainer}>
<View style={styles.textContainer}>
<Text style={styles.title}>{data.nid}</Text>
<Text style={styles.description} numberOfLines={0}>{data.title}</Text>
</View>
</View>
<View style={styles.separator}></View>
</View>
);
///////////
var cid = [];
var content = [];
for(let x=0; x < Object.keys(data.course).length; x++){
cid[x] = data.course[x].course_id;
content.push(
<TouchableHighlight
underlayColor='#e3e0d7'
key={x}
onPress={()=> {
this.rowPress(x); ///// here i need to redirect user to another page to show course's details
}}
style={styles.child}
>
<Text style={styles.child}>
{data.course[x].title}
</Text>
</TouchableHighlight>
);
}
var clist = (
<View style={styles.rowContainer}>
{content}
</View>
);
////////////
return (
<Accordion
header={header}
content={clist}
easing="easeOutCubic"
/>
);
}
I want to redirect user to a page and show details, and i want to have a header title and a back button (like Navigator or NavigatorIOS).
I've found a sample app that is doing exactly what i'm looking for, but it's too complicated for me, that i didn't understand how it is working,
react native tabbar-navigator (iOS)
I've looked into many examples and tutorials. i tried to implement their code into mine but it didn't work.
Also find some other tutorials using redux, but it's so complicated and hard for me. i'm looking for something more simpler.
Navigation using Redux
Looking forward to hearing from you,
Any help is highly appreciated. Thanks in Advance!
I found out that when i have few scenes and i want to navigate between them , best practice is to use Navigator or NavigatorIOS.
I continue my app and did few more scenes and components ( with few levels going deep ) and i found out that Navigator or NavigatorIOS is not working properly for that purpose. Then i switched to NavigationExperimental and that was awesome. Then i had some problems in state management, i found out about redux. it was not easy at first, but when i start it, you will love it.
So for newbies like me, it's better to start with redux at first. that would make life much easier for you :)

React Native Network Image Doesn't Render

The code I am writing is for wordpress.
I have succeed in fetching titles, category IDs and featured media IDs. Then I try to fetch the image uri to display it.
The wired thing is: Every time the post title and category id and author id come very fast, however, the image doesn't show until I press load more (which is built in by Gifted Listview). The requests happen both at "load more" and "ListViewOnFresh", so it should be like this.
I tried to use console.log to output "start loading" and "loading finished" when image starts and ends ending. However, I can see it was never loaded unless I press load more.
Even if it is loaded, if I pull to refresh (call the ListViewOnRefresh), it will disappear again.
I tried also to output dataRows, it has no problem at all, the featured_image is showing the right uri.
I really cannot solve this. Thank you in advance for your help.
var MovieListScreen = React.createClass({
componentDidMount: function() {
Controllers.NavigationControllerIOS("movies_nav").setLeftButtons([{
title: "Categories",
onPress: function() {
Controllers.DrawerControllerIOS("drawer").toggle({side:"left"});
}
}]);
},
renderListViewRow: function(row){
return(
<View >
<TouchableHighlight underlayColor={'#f3f3f2'} onPress={()=>this.selectRow(row)}>
<View style={styles.articleContainer}>
<View style={styles.rowDetailsContainer}>
<Image resizeMode="cover" style={styles.featuredImage}
source={{uri: row.featured_image}}
onLoadStart={() =>{console.log('start loading')}}
onLoadEnd={() => {console.log('loading finished')}}
/>
<Text style={styles.articleTitle}>
{row.title.rendered}
</Text>
<Text style={styles.articleTime} >
Posted by {row.author}, Category: {row.categories[0]}
</Text>
<Text style={styles.articleExcerpt}>
{row.excerpt.rendered}
</Text>
</View>
</View>
</TouchableHighlight>
<View style={styles.separator}/>
<View style={styles.articleActions}>
<Icon style={{flex:1}} name="share-alt" size={20} color="#0088CC" />
<Icon style={{flex:1}} name="thumbs-o-up" size={20} color="#0088CC" />
<Icon style={{flex:1}} name="star-o" size={20} color="#0088CC" />
<View style={{flexDirection:'row',justifyContent:'center',alignItems:'center'}}><Icon style={{flex:1}} name="external-link" size={20} color="#0088CC" /><Text style={{fontSize:15,color:'#0088CC'}}> Read More</Text></View>
</View>
</View>
);
},
listViewOnRefresh: function(page, callback){
var rowsData = [];
var REQUEST_URL = 'http://jo.wtf/wp-json/wp/v2/posts?per_page=10&order=asc&page='+page;
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {responseData.map((obj)=>{
fetch('http://jo.wtf/wp-json/wp/v2/media/'+obj.featured_media)
.then((responseMedia) => responseMedia.json())
.then((responseDataMedia) => {
obj.featured_image= responseDataMedia.guid.rendered;
})
rowsData.push(obj);
console.log(rowsData);
})
callback(rowsData);
return;
})
.done();
},
selectRow: function(row){
var navigationController = Controllers.NavigationControllerIOS("movies_nav");
navigationController.push({
component: "PushedScreen", // the unique ID registered with AppRegistry.registerComponent (required)
backButtonTitle: "", // override the nav bar back button title for the pushed screen (optional)
backButtonHidden: false, // hide the nav bar back button for the pushed screen altogether (optional)
});
},
render: function() {
return (
<RefreshableListView renderRow={(row)=>this.renderListViewRow(row)}
onRefresh={(page, callback)=>this.listViewOnRefresh(page, callback)}
backgroundColor={'#EFEFEF'}
style={styles.listview}/>
);
},
});
i think you should add
style={{width:200,height:300}}
to your Image tag
I have solved this problem in another way.
I tried to rewrite the code with async functions but it didn't help either. I think it is caused by 2 api calls at the same time, although why I cannot do that.
Anyway, I don't want to call one API to fetch the thumbnail ID and then call N APIs to get the url for those thumbnails, so I changed the wordpress API.
Add some code to the function.php file into my theme solved it.
It seems I cannot insert php code here... See here:
http://jo.wtf/adding-extra-fields-to-wp-api-output/

Categories