How to call a component onPress TouchableOpacity in react-native - react-native

I am using TouchableOpacity from react-native. The code is:
<TouchableOpacity onPress={onPress}>
<Text style={styles.supplementItem}>{item.item}</Text>
</TouchableOpacity>
where the OnPress function is as:
const onPress = () => (
// eslint-disable-next-line no-sequences
<Text style={styles.supplementItem}>Hello</Text>
this.setState({tempKey: tempKey + 1})
);
I looked at: this question and tried to do like it. But this is not working. I am setting my states as follows:
constructor(props) {
super(props);
this.state = {
tempKey: 0,
};
}
Kindly help me what I am doing wrong.

The question which you mentioned is using function based components and you are using class based components as you showed the costructor part which proves that.
So onPress must be a method in that class in your case, so you don't need the const keyword before it and you need to call it in this way ; this.onPress
In nutshell, your whole component should be like this;
import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';
class YourComp extends React.Component {
constructor(props) {
super(props);
this.state = {
tempKey: 0,
show: false
};
}
onPress = () => {
// eslint-disable-next-line no-sequences
this.setState(prevState => ({tempKey: prevState.tempKey + 1}))
};
render() {
return (
<View>
<TouchableOpacity style={{height: 100, justifyContent: 'center', alignItems: 'center'}} onPress={() => this.onPress()}>
<Text>Hello (item.item in your case)</Text>
</TouchableOpacity>
<Text key={this.state.tempKey.toString()}>Hello {this.state.tempKey}</Text>
</View>
)
}
}
export default YourComp;
If you want to show some component conditionally;
this.state = {
...,
show: false
}
Then in the onPress method;
this.setState(prevState => ({show: !prevState.show})) // this will make <Text /> to toggle the modal when clicked.
Then in the render method;
<Text>
{this.state.show && (
<YourNewComponent />
) || null}
</Text>

Related

How to execute onPress on TouchableOpacity react-native using jest and #testing-library/react-native?

I have a component called Header that look like this:
import React from 'react'
import {StyleSheet, TouchableOpacity, View, StyleProp, ViewStyle} from 'react-native'
import {Text} from '..'
import Icon from 'react-native-vector-icons/MaterialIcons'
import {theme} from '#app/presentations/utils/styles'
import {useNavigation} from '#react-navigation/core'
interface IHeaderProps {
title: string
headerRight?: () => JSX.Element | false | undefined
onGoBack?: () => void
hideBackButton?: boolean
style?: StyleProp<ViewStyle>
}
const Header: React.FC<IHeaderProps> = props => {
const navigation = useNavigation()
const goBack = () => {
props.onGoBack ? props.onGoBack : navigation.goBack()
}
return (
<View style={[styles.container, props.style]}>
<View style={styles.leftContent}>
{props?.hideBackButton ? null : (
<TouchableOpacity onPress={goBack} testID="headerBackButton">
<Icon name={'chevron-left'} size={22} color={theme.colors.black} />
</TouchableOpacity>
)}
</View>
<View style={{flex: 1, flexGrow: 10, alignItems: 'center'}}>
<Text maxLines={2} style={{paddingHorizontal: 8, textAlign: 'center'}} type="semibold">
{props.title}
</Text>
</View>
<View style={styles.rightContent}>{props.headerRight && props.headerRight()}</View>
</View>
)
}
export default Header
Focus on TouchableOpacity, I want to fire the onPress of it using testId, but looks like it won't fire.
it('Should have correct behavior', () => {
const goBackFn = jest.fn()
const props: IHeaderProps = {
title: 'My Header',
onGoBack: goBackFn,
}
const {component, getByTestId, queryAllByText} = renderComponent(props)
expect(component).toMatchSnapshot()
expect(queryAllByText('My Header').length).toBe(1)
expect(getByTestId('headerBackButton')).toBeTruthy()
fireEvent.press(getByTestId('headerBackButton'))
expect(goBackFn).toBeCalled()
})
The error message was like this
means that my goBack function never executed. I wondering why.
Then I check the snapshots of my Header component, it is not show TouchableOpacity but it shows View with onClick function on it
<View
accessible={true}
collapsable={false}
focusable={true}
nativeID="animatedComponent"
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
testID="headerBackButton"
>
My question is how do I execute onPress on TouchableOpacity ?
I fixed this. At least there is two problem from my implementation.
On the Header component, I forgot to add parenthesis () on props.onGoBack function. It should be props.onGoBack() not props.onGoBack
I need to add await waitFor(() => { ...wait for my getTestById to be truthy })

setAccessibilityFocus using ref not working

I'm using the ref prop along with findNodeHandle on a bunch of components in order to be able to trigger AccessibilityInfo.setAccessibilityFocus. However, it's not always working as expected. Sometimes the reference is null even though componentDidMount has executed.
I'm often using setAccessibilityFocus in order to focus the header of a new element which appears on the screen, for example when opening a modal.
IMPORTANT: This is Voiceover/Talkback functionality so you'll need to have that activated on your device.
See my snack: https://snack.expo.io/#insats/example-accessibilityinfo-setaccessibilityfocus-not-working
This is the code sample:
import React, { Component } from 'react';
import {
View,
Text,
findNodeHandle,
TouchableOpacity,
AccessibilityInfo,
StatusBar,
} from 'react-native';
class Sample extends React.Component {
constructor(props) {
super(props);
this.accessibilityRef = null;
}
componentDidMount() {
console.log('componentDidMount');
this.setAccessibilityFocus();
}
setAccessibilityRef(el) {
console.log('setAccessibilityRef', el);
this.accessibilityRef = el;
}
setAccessibilityFocus() {
console.log('setAccessibilityFocus', this.accessibilityRef);
if (this.accessibilityRef) {
const reactTag = findNodeHandle(this.accessibilityRef);
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
render() {
console.log('Rendering Sample');
return (
<Text ref={this.setAccessibilityRef}>
This text ought to be read out loud by the screenreader if enabled
</Text>
);
}
}
export default class App extends React.Component {
state = {
open: false,
};
toggle = () => this.setState({ open: !this.state.open });
render() {
return (
<View style={{ margin: 50 }}>
<StatusBar hidden />
<TouchableOpacity
style={{ backgroundColor: 'blue', padding: 20, marginBottom: 20 }}
onPress={this.toggle}>
<Text style={{ color: 'white' }}>
{this.state.open ? 'Hide text' : 'Show text'}
</Text>
</TouchableOpacity>
{this.state.open && <Sample />}
</View>
);
}
}
I don't really understand what is causing these issues. I've found that calling the setAccessibilityFocus twice solves the problem. You can simplify the logic of focusing by just handling everything in the callback ref as well.
Example:
export default () => {
const setInitFocus = (element: React.Component | null) => {
if (element == null) return;
const elementId = findNodeHandle(element);
if (elementId) {
AccessibilityInfo.setAccessibilityFocus(elementId);
AccessibilityInfo.setAccessibilityFocus(elementId);
}
};
return (
<TouchableOpacity
onPress={() => {}}
ref={setInitFocus}
>
<Text>Blah blah</Text>
</TouchableOpacity>
);
};
Here's your snack with those changes applied:
https://snack.expo.io/#loganlim/example-accessibilityinfo-setaccessibilityfocus-not-working

React-Native: Setting focus to custom component built off an array

I am trying to create a list of custom inputs based on an array, and when pressing the the enter key, I'd like the focus to automatically move to the next custom input. I can get this to work with a regular <TextInput> react component using the ref and onSubmitEditing but I cannot get this to function properly using my custom component that wraps a <TextInput>
Here is my code, it consists of two files: App.js and TextInput2.js (I know that currently the last line will error because of the reference counter but if I can get it to work I'll address the last issue)
Working Snack
-- App.js --
import React from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
import TextInput2 from './TextInput2'
export default class App extends React.Component {
constructor(){
super();
this.myRef = [];
this.state = {}
}
focusField = (key) => {
this.myRef[key].focus()
}
render() {
let textFields = ["one", "two", "three", "four", "five"];
return (
<View style={styles.container}>
{
textFields.map((x, i) => {
this.myRef[i] = React.createRef();
let k = i + 1
return(
<TextInput2
name={x}
key={i}
placeholder={x + " This Doesnt Work"}
ref={ref => this.myRef[i] = ref}
nextRef={this.myRef[k]}
//onSubmitEditing={() => this.focusField(k)}
//onSubmitEditing={() => this.myRef[k].focus()}
blurOnSubmit={false}
/>
)
})
}
{
textFields.map((x, i) => {
this.myRef[i] = React.createRef();
return(
<TextInput
name={x}
key={i}
placeholder="This works!"
ref={ref => this.myRef[i] = ref}
onSubmitEditing={() => this.focusField(i+1)}
blurOnSubmit={false}
/>
)
})
}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
--TextInput2.js --
import React from 'react';
import { View, TextInput } from 'react-native';
export default class TextInput2 extends React.Component {
state={}
handleFocus = () => {}
handleBlur = () => {}
focus() {
this.props.nextRef.focus()
}
render() {
return (
<View >
<TextInput
{...this.props}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onSubmitEditing={() => this.focus()}
/>
</View>
)
}
}
I've read this post and this but cannot seem to determine how to setup the function to set focus on the next field.
I have edited the Snack. Please try this
I think you're making it complicated. Try to change like this,
this.myRef[index] = React.createRef()
CustomTextComponent component
<CustomTextComponent
name={Something}
key={index}
forwardRef={this.myRef[index]}
onSubmitEditing={() => this.myRef[index + 1].current.focus()}
/>
As you're using createRef() you have to call it's ref using the "current" object.
CustomComponent.js
import React from 'react';
import { View, TextInput } from 'react-native';
export default class CustomComponent extends React.Component {
render() {
return (
<View >
<TextInput
{...this.props}
returnKeyType={"next"}
ref={this.props.forwardRef}
onSubmitEditing={this.props.onSubmitEditing}
/>
</View>
)
}
}

Update an input field in the webview from react-native component

I have a webview component like this:
export default class Home extends React.Component {
onMessage(m) {
//Update an input field in the webview with a custom value
}
render(){
let jsCode = '';
return (
<WebView
ref={(webView) => { this.webView.ref = webView; }}
injectedJavaScript={jsCode}
url={this.state.url}
onMessage={m => this.onMessage(m)}
/>
)
}
}
The webpage has an input field with an id='inpOne'. onMessage prop is triggered by a button click inside the webpage. How to update the input field with the above id when the onMessage prop is executed?
Stripped most of the code for brevity.
Probably like this.
export default class Home extends React.Component {
onMessage(m) {
const js = `document.getElementById('inpOne').value = ${m};`
this.webView.injectJavaScript(js);
}
}
Also, check your WebView's ref prop definition. It looks incorrect. Should be ref={ref => (this.webView = ref)}
Here is the full code of how to change HTML inside of WebWiew from React Native
import React, { Component } from 'react';
import { Text, View, TouchableHighlight } from 'react-native';
import { WebView } from 'react-native-webview';
export default class Sample extends Component {
constructor(props) {
super(props);
}
sendDataFromReactNativeToWebView() {
let injectedData = `document.getElementById("login_field").value = 'xyz#github.com';`;
this.webView.injectJavaScript(injectedData);
}
render() {
return (
<View style={{ flex: 1, marginTop: 30 }}>
<TouchableHighlight style={{ padding: 10, backgroundColor: 'gray', marginTop: 20 }} onPress={() => this.sendDataFromReactNativeToWebView()}>
<Text style={{ color: 'white' }}>Send Data To WebView from React Native</Text>
</TouchableHighlight>
<WebView
style={{ flex: 1 }}
source={{ uri: 'https://github.com/login' }}
ref={(webView) => this.webView = webView}
/>
</View>
);
}
}

Adding a View By Clicking a Button- REACT NATIVE

How can I add a view to the bottom of the screen with the click of a button, which is at the top of the screen, in React Native?
you make the view at the bottom of the screen conditionally render based on a state and you set it that state to be true on the OnPress method of the button
I think it is better to learn more about state and props and other fundamental concepts in react/react-native first:
https://facebook.github.io/react-vr/docs/components-props-and-state.html
but here is how you can do this:
You need to define a state if you can view that section as
false
, then when the button pressed, change the value of that state to
true
import React from 'react';
import {Text,View,TouchableOpacity} from 'react-native';
export default class testComponent extends React.Component {
constructor(){
super()
this.state = {
viewSection :false
}
}
renderBottomComponent(){
if(this.state.viewSection) {
return (
<View>
<Text>Hi!</Text>
</View>
)
}
}
buttonPress=()=>{
this.setState({viewSection:true})
}
render() {
return (
<View >
<TouchableOpacity onPress={this.buttonPress}>
<Text> Click Me!</Text>
</TouchableOpacity>
{this.renderBottomComponent()}
</View>
);
}
}
You can try this,
According to my knowledge this is what you want
import React, { useState } from 'react';
import { Button, View } from 'react-native';
export default function Testing() {
const Square = () => (
<View style={{
width: 50,
height: 50,
margin: 10,
backgroundColor: "green",
}} />
);
const [squares, setSquares] = useState([<Square />, <Square />, <Square />]);
return (
<View >
{squares.map(v => v)}
<Button title='add' onPress={() => setSquares([...squares, <Square />])} />
</View >
)
}