I'd like to have a context menu triggered on long press different places using React Native.
I.e. in a dialer like the default dailer. You can long-click on any contact and get a 'copy number' menu. And also you can long-click on the name of the person once you've opened their 'contact card'.
The straight-forward way needs a lot of copy-pasted boilerplate, both components and handlers.
Is there a better pattern for doing this?
All Touchable components (TouchableWithoutFeedback, TouchableOpacity etc.) has a property called onLongPress. You can use this prop to listen for long presses and then show the context menu.
To eliminate code mess and doing lots of copy paste you can separate your context menu as a different component and call it when the long press happen. You can also use an ActionSheet library to show the desired options. React native has a native API for iOS called ActionSheetIOS. If you get a little bit more experience in react and react-native you can create a better logic for this but I'm going to try to give you an example below.
// file/that/contains/globally/used/functions.js
const openContextMenu = (event, user, callback) => {
ActionSheetIOS.showActionSheetWithOptions({
options: ['Copy Username', 'Call User', 'Add to favorites', 'Cancel'],
cancelButtonIndex: [3],
title: 'Hey',
message : 'What do you want to do now?'
}, (buttonIndexThatSelected) => {
// Do something with result
if(callback && typeof callback === 'function') callback();
});
};
export openContextMenu;
import { openContextMenu } from './file/that/contains/globally/used/functions';
export default class UserCard extends React.Component {
render() {
const { userObject } = this.props;
return(
<TouchableWithoutFeedback onLongPress={(event) => openContextMenu(event, userObject, () => console.log('Done')}>
<TouchableWithoutFeedback onLongPress={(event) => openContextMenu(event, userObject, () => console.log('Done'))}>
<Text>{userObject.name}</Text>
<Image source={{uri: userObject.profilePic }} />
</TouchableWithoutFeedback>
</TouchableWithoutFeedback>
);
}
}
Similarly as the previous answer combine onLongPress with imperative control for popup menu - something like
<TouchableWithoutFeedback onLongPress={()=>this.menu.open()}>
<View style={styles.card}>
<Text>My first contact name</Text>
<Menu ref={c => (this.menu = c)}>
<MenuTrigger text="..." />
<MenuOptions>
// ...
</MenuOptions>
</Menu>
</View>
</TouchableWithoutFeedback>
When it comes to a lot of boilerplate - in React you can do your own components that you can reuse everywhere thus reducing boilerplate (and copy&paste)
See full example on https://snack.expo.io/rJ5LBM-TZ
Related
Unable to display the text entered in the Eventform and show it on the flatlist of EventList page.I am new to react native just need some help to solve the problem. Learning to develop a notes application.
EventForm js I am entering the text in it which has name location and notes field but when press add button does not showing it in the flatlist.
class EventForm extends Component {
state = {
title: null,
date: '',
};
handleAddPress = () => {
saveevents(this.state)
// console.log('saving events:', this.state);
.then(()=> this.props.navigation.goBack());
}
// handlePress() {
// this.props.onBtnPress()
// }
// }
handleChangeTitle = (text) => {
this.setState({ title: text });
}
handleChangeLocation =(text) => {
this.setState({location: text});
}
render() {
return (
<View
style={{
flex: 1
}}
>
<View style={styles.fieldContainer}>
<TextInput
style={styles.text}
placeholder="Name"
spellCheck={false}
value={this.state.title}
onChangeText={this.handleChangeTitle}
/>
<TextInput
style={styles.text}
placeholder="Enter your Location"
spellCheck={false}
value={this.state.location}
onChangeText={this.handleChangeLocation}
/>
<AutoExpandingTextInput
placeholder="Write your Notes here"
spellCheck={false}
style={[styles.default, {height: Math.max(35, this.state.height)}]}
value={this.state.text}
/>
</View>
<TouchableHighlight
onPress ={() =>this.handleAddPress.bind(this)}
// onPress={this.handleAddPress}
style={styles.button}
>
<Text style={styles.buttonText}>Add</Text>
</TouchableHighlight>
</View>
EventList js where I would like to display the dynamic text entered in EventForm should appear in flatlist. I am saving the flatlist content in db json file and calling it in the EventList.
const styles = StyleSheet.create({
list: {
flex: 1,
paddingTop: 20,
backgroundColor: '#A9A9A9'
},
});
class EventList extends Component {
state = {
events: [],
}
componentDidMount() {
//getEvents().then(events => this.setState({ events }));
const events = require('./db.json').events;
this.setState({ events });
}
render() {
return [
<FlatList
key="flatlist"
style={styles.list}
data={this.state.events}
renderItem={({ item, seperators }) => (<EventCard events=
{item} />)}
keyExtractor={(item, index) => index.toString()}
/>,
My db json file is this which I have manually entered the details.Data that is dynamically entered have to save in db json file and should reflect in the flatlist.
{
"events":[
{
"name":"chandu",
"date":"15-06-2018 ",
"location":"Sydney",
"content":"How are you..?",
"id":"05dafc66-bd91-43a0-a752-4dc40f039144"
},
{
"name":"bread",
"date":"15-07-2018 ",
"location":"Sydney",
"content":"I bought bread..?",
"id":"05dafc66-bd91-43a0-a752-4dc40f039145"
}
]
}
I am expecting whatever the text that is entered in the form should save and show me on the Flatlist which is in EventList file. Please help if you get any solution for it.
I have added alert message when onPressed to show that whether it is pressed or not.
I am expecting whatever the text that is entered in the form should save and show me on the Flatlist which is in EventList file. Please help if you get any solution for it.
I have added alert message when onPressed to show that whether it is pressed or not.
Your EventForm and EventList are two separate component without any shared props.
Assuming you are updating db.json file (using file read/write methods) from EventsForm component, still in the EventsList the data is fetched only once on componentDidMount.
You will need to pass the data for EventList as props or keep in state. If you keep it in props, then you must update the value of state using some life cycle method such as getDerivedStateFromProps props or something similar.
Edit:
I would suggest using global state management library like redux, for saving your data entered in the EventForm component, instead of read/write in a file.
You can save the data by dispatching an action in your EventForm component.
Later access that data in you EventList component using
this.props.your_db_name
You can save this data even on app kill using redux-persist
If you still want to continue with file read/write, you can look at this library react-native-fs
I'm stuck for weeks on this problem that I can't solve. I have been through a lot of different solution and none of them is working..
Let-s say I have component A and component B
Component A : contains a flatlist, when click on one item leads me to component B.
Component B: contains details of component A items, when clicking on a button update data, data that component A is using in state current, which make the clicked items turn to orange. I would like when hitting back button to update those data in component A and actually have the clicked items in orange, right now nothing is happening..
Component A :
async interventionList(){
const planningList = await getInterventionList(this.state.userToken, '2019-02-27', '2019-02-27');
this.setState({list: planningList});
}
renderItem = ({ item }) => (
<View>
<TouchableHighlight onPress={
() => this.props.navigation.navigate('InterventionStart', { getIntervention: () => this.interventionsList() })}>
<View style={[styles.block, this.state.current !== null && this.state.current.remoteInterventionId === item.num ? styles.began : styles.notbegan]}>
<Text> {SomeData} <Text>
</View>
</View>
</TouchableHighlight>
</View>
)
Component B :
componentWillUnmount() {
this.props.navigation.state.params.getIntervention();
}
But nothing happens. I tried to console log, to see if those functions where called after clickin on back button and they are... So I don't understand what I am doing wrong or is it not the way to do this at all ?
You can use willFocus method of react-navigation
in your componentDidMount or in your constructor
this.willFocus = this.props.navigation.addListener('willFocus', () => {
this.interventionList();
});
It will call your interventionList method whenever your component is about to be focused. Make sure you remove the listener on your componentWillUnmount
this.willFocus.remove();
For reference
https://reactnavigation.org/docs/en/navigation-events.html
I'm trying to call a function that will fire upon onFoucs on TextInput that will scroll the scrollView all the way down (using scrollToEnd())
so this is my class component
class MyCMP extends Component {
constructor(props) {
super(props);
this.onInputFocus = this.onInputFocus.bind(this);
}
onInputFocus() {
setTimeout(() => {
this.refs.scroll.scrollToEnd();
console.log('done scrolling');
}, 1);
}
render() {
return (
<View>
<ScrollView ref="scroll">
{ /* items */ }
</ScrollView>
<TextInput onFocus={this.onInputFocus} />
</View>
);
}
}
export default MyCMP;
the component above works and it does scroll but it takes a lot of time ... I'm using setTimeout because without it its just going down the screen without calculating the keybaord's height so it not scrolling down enough, even when I keep typing (and triggering that focus on the input) it still doesn't scroll all the way down.
I'm dealing with it some good hours now, I did set the windowSoftInputMode to adjustResize and I did went through some modules like react-native-keyboard-aware-scroll-view or react-native-auto-scroll but none of them really does the work as I need it.
any direction how to make it done the right way would be really appreciated. thanks!
Rather than using a setTimeout you use Keyboard API of react-native. You add an event listener for keyboard show and then scroll the view to end. You might need to create some logic on which input is focused if you have more than one input in your component but if you only have one you can just do it like the example below.
Another good thing to do is changing your refs to functional ones since string refs are considered as legacy and will be removed in future releases of react. More info here.
class MyCMP extends Component {
constructor(props) {
super(props);
this.scroll = null;
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
}
_keyboardDidShow() {
this.scroll.scrollToEnd();
}
render() {
return (
<View>
<ScrollView ref={(scroll) => {this.scroll = scroll;}}>
{ /* items */ }
</ScrollView>
<TextInput />
</View>
);
}
}
export default MyCMP;
If you have a large dataset React Native docs is telling you to go with FlatList.
To get it to scroll to bottom this is what worked for me
<FlatList
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: true, index: -1 }, 200);
}}
/>
I'm a new to react-native and i need a help. I want to do this: I have a button, when click it, a list item will show under the button. Help me guys !
Thanks
I suggest you to use or learn (if you want make your own popover) from react-native-list-popover. Here some screenshot from reace-native-list-popover:
Make a Boolean flag in your component state and initiate it with false and then use it for showing the list. You can use FlatList for make a good list.
The example code can be like this:
export default class ClassName extends Component {
state = {showList: false}
_onPress = () => {
this.setState({showList: true})
}
render() {
return (
<View>
<Button onPress={this._onPress}>Show List</Button>
{(this.state.showList) && <FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>}
</View>
)
}
}
My Problem is that I would like to navigateBack() from the BountyDetailsScreen to the LoyaltyScreen, but the navigateBack() function call does not trigger any action. When I log the function it says:
The only thing I notice is, that the navigationStack is empty. When I do the same with the navigateTo function it is working, but then I have a messed up navigation stack.
In my LoyaltyScreen.js I am displaying a ListView. It is a RN ListView (not imported from shoutem).
LoyaltyScreen.js
renderRow(bounty) {
return (
<ListBountiesView
key={bounty.id}
bounty={bounty}
onDetailPress={this.openDetailsScreen}
redeemBounty={this.redeemBounty}
/>
);
}
ListBountiesView.js
The ListBountiesView renders each ListView Row and opens a Detail Screen when clicked on the Row.
render() {
const { bounty } = this.props;
return (
<TouchableOpacity onPress={this.onDetailPress}>
{bounty.type == 0 ? this.renderInShopBounty() : this.renderContestBounty()}
<Divider styleName="line" />
</TouchableOpacity>
);
}
BountyDetailsScreen.js
In the BountyDetailsScreen I display detailed information and would like to navigateBack() to the Loyalty Screen when I press a button.
<Button styleName="full-width" onPress={() => this.onRedeemClick()}>
<Icon name="add-to-cart" />
<Text>Einlösen</Text>
</Button>
onRedeemClick() {
const { bounty, onRedeemPress } = this.props;
onRedeemPress(bounty);
navigateBack();
}
navigateBack is an action creator. You need to map it to props and read it from props in your redeemClick function. Just executing the imported action creator won't do anything since it's not connected to Redux.
Here's an example of you map it to props:
export default connect(mapStateToProps, { navigateBack })(SomeScreen));
Here's how you use it:
const { navigateBack } = this.props;
navigateBack();
I can see that airmiha's answer is what you're looking for, but I just wanted to add onto it.
You can also use hasHistory to set up your #shoutem/ui NavigationBar (if you're using it) with a simple back button that utilises navigateBack().
<NavigationBar
styleName="no-border"
hasHistory
title="The Orange Tabbies"
share={{
link: 'http://the-orange-tabbies.org',
text: 'I was underwhelmed by The Orange Tabbies, but then I looked at that
sweet, sweet back button on the Nav Bar.
#MakeNavBarsGreatAgain',
title: 'Nevermind the cats, check the Nav Bar!',
}}
/>
You can find more examples with the NavigationBar component here.