why does the touchable opacity onPress not trigger? - react-native

I am having an issue where my onPress of a touchable opacity doesn't fire. I am sure it isn't working because nothing console logs to the system when I press it.
My button component:
const FloatingPlusButton = (props) => {
return (
<View style={styles.buttonStyle}>
<TouchableOpacity onPress={props.tapToAddEvent}>
<MaterialIcons
name='add'
size={45}
color='#28313b'
/>
</TouchableOpacity>
</View>
);
};
Where I call it:
class HomeScreen extends Component {
constructor() {
super();
this.state = {
textInput: '',
inputVisible: true
};
}
onFloatingButtonPress() {
this.setState({ inputVisible: true }, () => { this.textInputField.focus(); });
console.log('p');
}
render() {
return (
<View style={{ flex: 1, height: HEIGHT }}>
{ !this.state.inputVisible &&
<FloatingPlusButton tapToAddEvent={this.onFloatingButtonPress.bind(this)} />
}
</View>
);
}
}
To be clear I do see the button and the inputVisible prop is not the issue. Just nothing happens when I press it. I tried it with both the .bind(this) and without it and neither worked.

I was able to figure it out. I had to make the zIndex of the button greater than the zIndex of the root view component. Thanks for the help everybody.

Related

How can I hide/show components by touching not button but screen on React Native?

I'm learning React Native for the first time. I want to implement a function to show/hide the component by touching the screen, not a specific button.
(Please check the attached file for the example image.)
enter image description here
In this code, I've tried to make a function. if I touch the screen (<View style={style.center}>, then show/hide the renderChatGroup() and renderListMessages() included in <View style={style.footer}>. The source code is below.
In my code, it works. However, the two <View> tag is not parallel. the footer view is center View's child.
I want to make them parallel. but I couldn't find the contents about controlling another <View> tag, not a child. In this code, I used setState, then I couldn't control another the below <View>.
Of course, I tried Fragment tag, but it didn't render anything.
How could I do implement this function? Please help me!
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
const { isVisibleFooter } = this.state;
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Firstly I would highly recommend you use react native with functional components and React Hooks as they alternative will soon will be deprecated.
Since onPress is not available on the View Component, you would need to replace it with TouchableWithoutFeedback as you have already done in your code.
For Showing/Hiding a view you would need to use a conditional operator.
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
const { isVisibleFooter } = this.state;
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
{isVisibleFooter && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Here you can see i have replaced
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
with
{isFooterVisible && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
stating that to only display the Footer View when
const isFooterVisible = true;

A way to Highlight <Pressable/> component in react-native?

So im using the Pressable Component in react-native instead of using Button because i've heard it has many limitations regarding styling. I've tried using TouchableHighlight as well but it isnt working porperly for me moreso since my Component already has some styling and Positioning adjusted into it
class randomComponent extends Component {
render() {
return (
<Pressable>
<Text>Hello World</Text>
</Pressable>
)
}
tl:dr How do i Highlight a Pressable Component? thanks
You can just use the "render prop" style instead if you want access to the pressed state without having to store your own useState variable.
From the docs:
<Pressable
style={({ pressed }) => [
{
backgroundColor: pressed
? 'rgb(210, 230, 255)'
: 'white'
},
styles.wrapperCustom
]}>
{({ pressed }) => (
<Text style={styles.text}>
{pressed ? 'Pressed!' : 'Press Me'}
</Text>
)}
</Pressable>
Solution:
import { TouchableOpacity } from 'react-native'
TouchableOpacity is better for developers with experience in a web-based background and styling.
You need to use a state variable to store the pressed state and change the component style accordingly
class randomComponent extends Component {
constructor(props) {
super(props)
this.state = {
pressed: false
}
}
render() {
return (
<Pressable
onPressIn={() => this.setState({pressed: true})}
onPressOut={() => this.setState({pressed: false})}
style={this.state.pressed ? styles.pressed : {}}
>
<Text>Hello World</Text>
</Pressable>
)
}
const styles = StyleSheet.create({
pressed: {
backgroundColor: 'red'
}
})

TextInput focus blurring when first clicked

There was no issue before I tried to implement the handleInputBlur and handleInputFocus functions (Used to change the background when in focus). When I first click on the TextInput it comes into focus, but then immediately blurs, resulting in the background flashing then disappearing. What's strange is that after this first click, the future clicks work absolutely fine, focusses and blurs as it should. I do not understand why on the initial click/focus it immediately blurs. Code below:
EDIT: Bit more context, it's inside of a modal, which contains multiple of these editable items.
class EditableItem extends Component {
constructor(props) {
super(props)
const { value } = this.props
this.state = {
value,
isFocused: null,
}
}
handleInputBlur = () => {
this.setState({ isFocused: false })
console.log('blurring')
}
handleInputFocus = () => {
this.setState({ isFocused: true })
console.log('focussing')
}
render() {
const { name, secure, children, autoCapitalize } = this.props
const { value, isFocused } = this.state
const multiline = !secure
return (
<View>
<View style={styles.container}>
<Text style={styles.name}>{name}</Text>
<View style={isFocused ? styles.activeBackground : styles.unfocusedBackground}>
<TextInput
placeholder={name}
placeholderTextColor={COLOR_BASE_3}
underlineColorAndroid="transparent"
style={styles.value}
secureTextEntry={secure}
value={value}
// blurOnSubmit
onSubmitEditing={() => {
Keyboard.dismiss()
}}
returnKeyType="done"
keyboardAppearance="dark"
autoCapitalize={autoCapitalize}
onChangeText={this.onChange}
multiline={multiline}
onBlur={() => this.handleInputBlur()}
onFocus={() => this.handleInputFocus()}
/>
{children}
</View>
</View>
<View style={styles.divider} />
</View>
)
}
onChange = value => {
const { onChange } = this.props
this.setState({ value })
onChange(value)
}
}
Ok so solved this by setting autofocus to true in the TextInput. Not sure why not having this set causes this issue but it's a solution regardless.

Better solution to open the Menu when 3 dots are clicked in React Native

I am able to open menu when 3 dots icon is clicked for each item. But can the code be written in a better way..
Right now menu is getting created for each card item but ideally it would have been good to create single Menu View and dynamically associate it to some card where ever the 3 dots is clicked.
Expo Source Code Link
Code
export default class App extends React.Component {
constructor(props, ctx) {
super(props, ctx);
this.state = {
list: [
{ name: "Michael", mobile: "9292929292", ref: React.createRef() },
{ name: "Mason Laon Roah", mobile: "1232313233", ref: React.createRef() },
{ name: "Constructor", mobile: "4949494949", ref: React.createRef() },
{ name: "Rosling", mobile: "4874124584", ref: React.createRef() }
],
};
}
_menu = null;
hideMenu = () => {
this._menu.hide();
};
showMenu = (ref) => {
this._menu = ref;
this._menu.show();
};
render() {
const renderItem = ({ item, index }) => (
<ListItem
title={
<View>
<Text style={{ fontWeight: "bold" }}>{item.name}</Text>
<Text>{item.mobile}</Text>
</View>
}
subtitle={
<View>
<Text>445 Mount Eden Road, Mount Eden, Auckland. </Text>
<Text>Contact No: 134695584</Text>
</View>
}
leftAvatar={{ title: 'MD' }}
rightContentContainerStyle={{ alignSelf: 'flex-start'}}
rightTitle={this.getMenuView(item.ref)}
/>
);
return (
<View style={styles.container}>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
showsVerticalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
data={this.state.list || null}
renderItem={renderItem}
ItemSeparatorComponent={() => (
<View style={{ marginBottom: 5 }} />
)}
/>
</View>
</View>
);
}
getMenuView(ref) {
return (
<Menu
ref={ref}
button={<Icon onPress={() => this.showMenu(ref.current)} type="material" color="red" name="more-vert" />}
>
<MenuItem onPress={this.hideMenu}>Menu item 1</MenuItem>
<MenuItem onPress={this.hideMenu}>Menu item 2</MenuItem>
<MenuItem onPress={this.hideMenu} disabled>
Menu item 3
</MenuItem>
<MenuDivider />
<MenuItem onPress={this.hideMenu}>Menu item 4</MenuItem>
</Menu>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Sample Output
As mentioned here, you can find an undocumented UIManager.java class that allows you to create Popups with its showPopupMenu method.
This currently works only for Android.
import React, { Component } from 'react'
import { View, UIManager, findNodeHandle, TouchableOpacity } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
const ICON_SIZE = 24
export default class PopupMenu extends Component {
constructor (props) {
super(props)
this.state = {
icon: null
}
}
onError () {
console.log('Popup Error')
}
onPress = () => {
if (this.state.icon) {
UIManager.showPopupMenu(
findNodeHandle(this.state.icon),
this.props.actions,
this.onError,
this.props.onPress
)
}
}
render () {
return (
<View>
<TouchableOpacity onPress={this.onPress}>
<Icon
name='more-vert'
size={ICON_SIZE}
color={'grey'}
ref={this.onRef} />
</TouchableOpacity>
</View>
)
}
onRef = icon => {
if (!this.state.icon) {
this.setState({icon})
}
}
}
Then use it as follows.
render () {
return (
<View>
<PopupMenu actions={['Edit', 'Remove']} onPress={this.onPopupEvent} />
</View>
)
}
onPopupEvent = (eventName, index) => {
if (eventName !== 'itemSelected') return
if (index === 0) this.onEdit()
else this.onRemove()
}
Source: https://cmichel.io/how-to-create-a-more-popup-menu-in-react-native
There is now a React Native plugin for this. I'm not sure it was around when the question was originally asked. But I'm leaving this here for anyone else looking for the answer.
https://www.npmjs.com/package/react-native-popup-menu
The example worked for me. I wanted to use the vertical ellipsis, so I did this modification to the MenuTrigger part of the example to an icon instead of text:
<MenuTrigger>
<Icon name="more-vert" size={25} color={colors.rustRed} />
</MenuTrigger>
As a side note, I had difficulty finding and using the ellipsis. I eventually went with using react-native-vector-icons by using 'npm -i react-native-vector-icons' and importing the Material Icons like this:
import Icon from 'react-native-vector-icons/MaterialIcons';
Use React Portals
https://reactjs.org/docs/portals.html
In short the receipts is:
You define your dynamic menu at sibling level only once in the parent i.e. in your case it would be adjacent to App.
Handle Click at each item level to open your component. You can pass some specific event days to achieve the dynamism.
Easier example https://codeburst.io/reacts-portals-in-3-minutes-9b2efb74e9a9
This achieves exactly what you are trying to do which is defer the creation of component untill clicked.

How to keep the previous state after navigation

//Home page
class Home extends Component {
constructor(props){
super(props);
this.state = {time: ""}
}
render(){
const { navigate } = this.props.navigation;
return(
<View>
<TextInput onChangeText={(time) => this.setState({ time })} placeholder = "enter time" />
<TouchableOpacity style={{marginTop: 10, height: 20, width: 50, borderRadius: 6, borderColor: 'white', backgroundColor:'red'}}
onPress = { () => {
navigate('Wait', {
time: this.state.time * 1000
})
}}
>
<Text style={{color: 'white'}}>add</Text>
</TouchableOpacity>
</View>
)
}
}
//Waitlist page
class Wait extends Component{
constructor(props){
super(props);
this.state = { data: []}
}
renderTime(){
return this.state.data.map((item) => {
return(
<View>
<TimerCountdown
initialSecondsRemaining={ item }
allowFontScaling={true}
style={{ fontSize: 20 }}
/>
</View>
)
})
}
updatestate(time){
var temp = this.state.data
temp.push(time)
this.setState({data: temp})
}
render(){
const { navigate } = this.props.navigation;
const { navigation } = this.props;
const time = navigation.getParam('time', 'NO-Name');
return(
<View>
<TouchableOpacity onPress = {()=>{ this.updatestate(time)}}>
<Text> Update </Text>
</TouchableOpacity>
<View>
{this.renderTime()}
</View>
</View>
)
}
}
I am trying to write a function to keep adding countdown timer to another page. After the user enter the input time, it should navigate to the waitlist page and update the state and render all the timers. The above code does not work because each time I navigate to that page the state will reset.
I also tried it with the AsyncStorage. I was able to add the time to the AsyncStorage on the home page and render each time on the waitlist page. But the problem is all the timers will reset every time I add a new timer.
I just want to know how can I keep the previous state after navigate to another page. Can someone please show me some examples? Thanks a lot.
Documentation for timer-countdown: https://www.npmjs.com/package/react-native-timer-countdown