I am doing a dictionary app with lists of items like this:
acceptable, benevolent, big, charitable, considerate
fair, good, helpful, honest, hospitable
lavish, reasonable, thoughtful, tolerant, unselfish
Each item in the list is a link which leads to the similar list related to the word clicked.
I have two questions:
How to do it in React Native w/o falling into caring hands of React Native WebView? It's required to support styling (as in picture) and handling targeted clicks somehow.
Alternative solutions are welcome, including those built upon the WebView component. Just to consider performance side in here.
P.S. I have spotted alike functionality in the M.-W. dictionary app:
According to doc:
Text supports nesting, styling, and touch handling.
So I think the best solution is to properly nest your texts and pass them a function to handle the onPress action.
I will give an example code, not styled at all but completely stylable:
onPress = (text) => {
// do stuff
return
}
render() {
return (
<View style={styles.container}>
<Card>
<Text>
Synonyms:
{this.state.synonyms.map(synonym => {
return <Text onPress={() => this.onPress(synonym)}> {synonym} </Text>
})}
</Text>
</Card>
</View>
);
}
And here is a snack if you wanna take a look
Related
The problem is pretty simple, still I can't figure out why it happens. I'd need some good Android and/or React Native developers.
I have a settings page in my application which renders 4 boxes (depending content of the settings, like user password, email modification...) and one of them has a bit much more content than the others.
The problem seems to only occur on Android 7.0 (testing with a real device, Honor 8) and it works well on One Plus 6 (8.1) and a Xiaomi (I don't know which one, running 7.1)
As long as I want to display all the content of the box, it will just not render to the screen, but still takes the height needed in the ScrollView to be displayed (see the screens)
At first, I didn't understand why, because no error was triggered, I decided to check the logCats and I got this error :
View: ReactViewGroup not displayed because it is too large to fit into a software layer (or drawing cache), needs 8636760 bytes, only 8294400 available
Imgur
So I tried multiple things:
set android:hardwareAccelerated="false" in the AndroidManifest, this solution works BUT the app is quite laggy when you scroll, and all is a bit slowed down (I would love to avoid this)
Delete some components from the render, which also works, but... obviously, I want all my content in this box
I tried to understand which component takes a lot of space (in bytes) by adding/deleting the components in the view and referring to the logCat, seems like ZestInput takes quite a lot, as custom components for Toggles, and Checkboxes (I also tried with native TextInput to compare with our custom input, the result was not that far... they bot take a lot of space)
render() {
const {
textStyle,
titleTextStyle,
subtleText,
boxStyle,
marginLarge,
marginSmall,
marginLeft,
buttonStyle,
} = styles;
const { isUpdating, newMail } = this.state;
const { user, emailEdition } = this.props;
const emailTranslation =
newMail === ''
? translations.t('ADD_EMAIL')
: translations.t('CHANGE_EMAIL');
return (
<MCollapsableBox
style={boxStyle}
headerText={translations.t('APPLICATION_SETTINGS')}
>
<View>
<Text style={[titleTextStyle, marginLarge]}>
{translations.t('PARAMETERS_CHOOSE_LANGUAGE')}
</Text>
{(newMail === '' || emailEdition) && (
<View>
<Separation />
<Text style={[titleTextStyle, marginLarge]}>
{translations.t('EMAIL')}
</Text>
<ZestInput
//ref={ref => (this.inputRef = ref)}
placeholder={emailTranslation}
onChangeText={this.changeMailAddress}
value={newMail}
/>
{user.tmpEmail !== '' && (
<View>
<Text style={[textStyle, marginSmall]}>
{translations.t('CURRENT_TMP_MAIL')}
</Text>
<Text style={[textStyle, marginLeft]}>{user.tmpEmail}</Text>
<Text style={[subtleText, marginLeft]}>
{translations.t('EMAIL_NEEDS_VALIDATION')}
</Text>
</View>
)}
<PrimaryButton
onPress={this.onChangeEmail}
style={buttonStyle}
isLoading={isUpdating}
disabled={!this.checkIsEmail(newMail)}
>
{emailTranslation}
</PrimaryButton>
</View>
)}
<Separation />
<Text style={[titleTextStyle]}>
{translations.t('PARAMETERS_VISIBILITY')}
</Text>
{_.map(this.state.privacyOptions, (value, key) => {
return (
<TitledTextToggle
title={value.text}
options={value.options}
onPress={this.toggleChanged}
key={key}
id={key}
/>
);
})}
<Separation />
<Text style={[titleTextStyle, marginSmall]}>
{translations.t('MOOD_NOTIFICATIONS_SETTINGS')}
</Text>
<Text style={[textStyle, marginLarge]}>
{translations.t('MOOD_NOTIFICATIONS_SETTINGS_TEXT')}
</Text>
<CheckboxList
onPress={this.onPressCheckbox}
options={this.state.moodNotification}
/>
<PrimaryButton
onPress={this.onSave}
style={buttonStyle}
isLoading={isUpdating}
>
{translations.t('SAVE')}
</PrimaryButton>
</View>
</MCollapsableBox>
);
}
This is what I expect
Imgur
Imgur
Imgur
I have my code setup as such:
export default class HomeScreen extends Component {
constructor() {
super();
}
componentDidMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
_keyboardDidShow = () => {
console.log('keyboard did show')
}
_keyboardDidHide = () => {
console.log('keyboard did hide')
}
render() {
return (
<Container styles={styles.container} >
<Content styles={styles.content} contentContainerStyle={marginLeft=this.state.marginLeft}>
<Image
style={styles.bgImg}
source={Images.bgImg}
>
</Image>
<Image
style={styles.logo}
source={Images.logo}
>
</Image>
<Text style={styles.slogan}>This is the title</Text>
<Form style={styles.search_form}>
<Item rounded floatingLabel style={styles.search}>
<Label style={styles.search_label}>Where are you headed?</Label>
<Input style={styles.search_input} />
<Button full rounded style={styles.search_btn}>
<Icon name="search"></Icon>
</Button>
</Item>
</Form>
</Content>
</Container>
);
}
}
I want basically the Content component of native-base to avoid the keyboard. I have my logo at the top, the slogan below it and the form at the bottom of the screen by giving some absolute positioning. At this point, the content component moves way up the screen which I don't want. What I want is the logo and the slogan staying right at the top of the screen but the form which is at the bottom of the page; to move up.
Here's what I've researched so far:
Found out that there is actually a component from react native called KeyboardAvoidingView and I played around with it but keeping the Logo, background image and the rest of the content inside the KeyboardAvoidingView made all the content not show in the screen.
Later I found out that the native base component 'Content' itself extends KeyboardAvoidingView, so there was no need to use it in the first place. But I don't think KeyboardAvoidingView is working with my versions of react native and native base.
So at last, I decided that this was a bug and I would use the Keyboard module of the react native instead to do some custom work, which is where I'm at right now, code-wise.
The Question
The console logging inside _keyboardDidShow and _keyboardDidHide are working, which means now I just need to know how to change the style of a component on keyboardDidShow and keyboardDidHide. Any help is appreciated, of course!
I'm really new to react native so any suggestions to better improve my workflow will be taken seriously.
I have already stumbled across the same problem! I'd recommend you save the keyboard width on state, with, for example:
keyboardDidShow = e => this.setState(p => ({ ...p, height: e.endCoordinates.height })
keyboardDidHide = () => this.setState(p => ({ ...p, height: 0 })
then, having this height, you can make your UI depend on that value. After you make sure that is working, in order for it not to jump between positions, use Animated to have a seamless transition between the positions. Hope this helps!
Hi I'm new in react native and I'm working on a sample chat app.Currently I'm facing a basic problem of navigation. Any help is appreciated.
I need to navigate from chat list to chat screen on click. Im using android emulator using Android studio. For this I wrote the script. But Im getting error when I click on a right_arrow having onPress function defined in the following code,
onContactPress = (item) =>{
Alert.alert();
}
renderFlatListItem({item}) {
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => this.onContactPress(item)} >
<Image style={styles.rightArrow}
source={require('../../images/right_arrow.png')}/>
</TouchableOpacity>
</View>
)
}
and the the error is
undefined is not an object (evaluating '_this3.onContactPress()')
What I'm doing wrong here? Is there a way to pass the contact details of the selected person to the next screen? Please help me out.
This is in a React Class o functional component ?
In case of the 2nd call simply onContactPress without this, if your case is the first maybe you have a babel issue.
ok try this solution
change your onContactPress
from
onContactPress = (item) =>{
Alert.alert();
}
to
onContactPress({item}) {
Alert.alert();
}
because fat arrow functions should be defined outside the class
You probably have solved the issue by now. I'm new to React-Native myself and I came across your question after facing the same issue and I realized that the answer was in front of me all along.
Error Prone Script
I have a component with a state that contains an array of names created using the constructor method.
Ideally, when rendered this is the layout I was expecting on my Android device A list of User names and from there since I have assigned a key attribute and the value of the name's array index, when a user name is clicked I wanted to console.log(i), (i) being the index provided as a argument to the getValue() function in the onPress={} event listener.
But instead this is what I was getting the TypeError: undefined is not an object (evaluating 'this.getValues') error.
I tried reading docs, I googled, I looked at previously working codes, I did everything and nothing worked.
Nothing worked until I changed my renderName method from this
getValues(i){
console.log(i);
}
renderName(){
return this.state.names.map(function(name,i){
return (
<View>
<TouchableHighlight
key={i}
style={styles.viewName}
onPress={() => this.getValues(i)}>
<Text style={styles.name}>{name}</Text>
</TouchableHighlight>
</View>
)
})
}
To this
getValues(i){
console.log(i);
}
renderName(){
return this.state.names.map((name,i) => {
return (
<View>
<TouchableHighlight
key={i}
style={styles.viewName}
onPress={() => this.getValues(i)}>
<Text style={styles.name}>{name}</Text>
</TouchableHighlight>
</View>
)
})
}
The only difference is that I used a fat arrow function this.state.names.map((name,i) => { instead of the normal one this.state.names.map(function(name,i){
After making that change the results are what I was expecting Clicking a user name output the index associated with the name in the array
This may not solve your problem but I'll state it anyway through the following points
I caused my problem by trying to be too familiar with a Library I've basically just started learning.
Not every new error should make us panic, that's when we start asking bunch of questions rather than being calm enough to realize that the cause of the error is in front of us.
Experimenting with code is a good thing while you are learning but make sure that the code works as the tutorial instructs and that you fully understand it before you start making changes and playing around with it.
According to me you might be using Functional Component and in functional component you can call methods directly without using this. Just add const keyword outside the method and call without this keyword.
OnPress takes a function, instead, you're sending an encapsulated function.
Try this
onContactPress = (item) =>{
Alert.alert();
}
renderFlatListItem({item}) {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.onContactPress(item)} >
<Image style={styles.rightArrow}
source={require('../../images/right_arrow.png')}/>
</TouchableOpacity>
</View>
)
}
What is the best practice to link the whole area below to another screen?
The whole area marked with orange needs to be clickable and takes to another screen. Should I put this whole area into a view? What is the best practice here? Would appreciate a code example if possible.
Yes, you can wrap that orange area using View component and try to use React Native Router Flux, the most popular and easiest way to navigate the screen/component.
import React, { Component } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { Actions } from 'react-native-router-flux';
...
export default class HelloWorldList extends Component {
...
render() {
return (
<TouchableOpacity onPress={() => Actions.componentkey}>
<View>
...
</View>
</TouchableOpacity>
);
}
}
Moving from one scene (screen) to another is called navigation, there are plenty of (good and bad) navigation components packages/modules out there and most of them have an Example app to test and learn.
I'd recommend React Navigation as it's really well documented and very easy to understand also it's covered in React Native docs as well.
Other packages:
react-native-router-flux
react-native-navigation
native-navigation
I am running react native 0.24.1 and I am experiencing an issue with the <TouchableOpacity> component when it is placed inside an <ScrollView>.
Its onPress events fire fine but there is a special case when they do not.
If along with the <TouchableOpacity> component you have a <TextInput>, and the current focus is on the <TextInput> box, then you may click on the <TouchableOpacity> and you will see its onPress event WILL NOT be fired.
At least the first time you do it. Once the focus is NOT on the <TextInput> anymore, you can now press on the <TouchableOpacity> component and its onPress event will fire just fine.
Note that if the <TouchableOpacity> component is placed inside a <View> instead of an <ScrollView> everything works as expected and the above issue does not apply.
Here is some code to demonstrate the problem:
const React = require('react-native');
const {
Component,
Dimensions,
View,
ScrollView,
Text,
TextInput,
TouchableOpacity,
} = React;
// ----------------------------------------------------------------------------
class TouchableOpacityTest extends Component {
constructor(props, context) {
super(props, context);
this.state = {count_onPress:0,count_onPressIn:0,count_onPressOut:0,count_onLongPress:0};
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
onPressEvent(what,e) {
console.log('what:',what);
let newState = {};
newState['count_'+what] = ++this.state['count_'+what];
this.setState(newState);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
render() {
let touchableProps = {
onPress: this.onPressEvent.bind(this,'onPress'),
onPressIn: this.onPressEvent.bind(this,'onPressIn'),
onPressOut: this.onPressEvent.bind(this,'onPressOut'),
onLongPress: this.onPressEvent.bind(this,'onLongPress'),
}
return (
<View style={{flex:1,flexDirection:'column',justifyContent:'flex-start',alignItems:'center',backgroundColor:'blue'}} >
<ScrollView style={{width:Dimensions.get('window').width*0.9,backgroundColor:'red'}}>
<TextInput style={{backgroundColor:'rgb(200,200,200)',marginTop:14}}
placeholder="Focus on me,hide keyboard,and click on text below"
autoCorrect={false}
/>
<TouchableOpacity {...touchableProps} >
<Text style={{fontSize:20,backgroundColor:'pink',marginTop:14}}>
Click on me!{"\n"}
onPress:{this.state.count_onPress}{"\n"}
onPressIn:{this.state.count_onPressIn}{"\n"}
onPressOut:{this.state.count_onPressOut}{"\n"}
onLongPress:{this.state.count_onLongPress}{"\n"}
</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// ----------------------------------------------------------------------------
AppRegistry.registerComponent('react_native_app1', () => TouchableOpacityTest);
You may replace the <ScrollView> with a <View> component on the above code and you will see that onPress event fires every time, even when the focus is on the <TextView>
NOTE: I am working on Android. I have no idea if this happens also on iOS.
NOTE 2: According to Aakash Sigdel, this is indeed happening on iOS too.
Set keyboardShouldPersistTaps={true} on your ScrollView.
Duplicate answer here: https://stackoverflow.com/a/34290788/29493
UPDATE: As Hossein writes in his answer, true|false has been deprecated in newer versions in favor of always|never|handled.
Set keyboardShouldPersistTaps='always' to your ScrollView props.
React Native Documentation:
'never' (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won't receive the tap.
'always', the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
'handled', the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor).
false, deprecated, use 'never' instead.
true, deprecated, use 'always' instead.
In my case, I was using alignItems:'baseline', when I switched to alignItems:'center', it started working smoothly. Don't know why