React Native TextInput onPressOut fires instantly - react-native

I am making a class for a custom TextInput, where the style will change when the field is selected, and will change back as soon as it is pressed out of. It looks as follows...
export function SoftSearchBar({
height=40,
width='100%',
fontSize=20,
fireOnChange={function(){console.log("No Change Function in place")}},
value=false,
placeholder="Placeholder",
type=null
}){
const [isActive, setActive] = useState(false)
const [style, setStyle] = useState({})
useEffect(() => {
console.log(isActive)
if (isActive){
setStyle(style => ({style: styles.softSearchActive, width: width}))
}
else{
setStyle(style => ({style: styles.softSearchInactive, width: width}))
}
}, [isActive])
return(
<View style={{height: height, flexDirection: 'row'}}>
<TextInput
value={value}
onPressIn={() => setActive(true)}
onPressOut={() => setActive(false)}
style={{...style.style, width: width, zIndex: 0, fontSize: fontSize}}
textContentType={type}
text
placeholder={placeholder}
placeholderTextColor={'black'}
autoCorrect={false}
onChangeText={text => {
fireOnChange(text)
}}
/>
</View>
)
}
Almost all of this works as expected, when the field is pressed, an outline appears indicating its selection, and the text changes color. However, onPressOut fires immediately after onPressIn, as the log will look like this as soon as I press the field
true
false
indicating that onPressOut fired, since it is the only way to setIsActive(false)
I saw some solutions recommending using onResponderRelease as opposed to onPressOut but then it just never unselects. Is there some syntax Im missing with onPressOut? This seems like a pretty simple and straightforward syntax so I am unsure

Main Issue with your code is onPressIn and onPressOut you need to change them to onFocus and onBlur
Here is a working example you can paste into this website
https://reactnative.dev/docs/textinput
You can set your default Input style and then when active you can enable the style you want.
outlineStyle: none to get rid of the default blue outline of the textinput when focused
Can also just remove handleFocus & handleBlur and move the function into the actual function calls to reduce the code further
import React from "react";
import { SafeAreaView, StyleSheet, TextInput } from "react-native";
const UselessTextInput = () => {
const [style, setStyle] = React.useState({borderWidth:2 , borderColor: 'red', outlineStyle: 'none'});
const [active, setActive] = React.useState(false)
const handleFocus = () => setActive(true)
const handleBlur = () => setActive(false)
return (
<SafeAreaView>
<TextInput
style={[styles.input, active && style]}
onFocus={handleFocus}
onBlur={handleBlur}
onChangeText={() => {}}
value={null}
placeholder="useless placeholder"
keyboardType="numeric"
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default UselessTextInput;

Related

react native onChangeText and onChange will give input value missing 1 character

I was wondering why there's 1 missing character every time I type on text input using onChangeText and onChange function simultaneously.
Here's my code:
import React, {useState} from "react";
import {Text} from "galio-framework";
export default function UpcomingSurvey ({ navigation }) {
const [timer, setTimer] = useState('')
const [saving, setSaving] = useState(false);
const [edited, setEdited] = useState(false);
const [inputValue, setInputValue] = useState('')
const inputChanged = () => {
setSaving(true)
clearTimeout(timer)
const newTimer = setTimeout(() => {
console.log('inputValue: ', inputValue)
}, 2000)
setTimer(newTimer)
}
function renderUpcomingSurvey () {
<Block style={{ paddingHorizontal: theme.SIZES.BASE }}>
<>
{saving &&
<Block style={{flex: 1}}>
<Text muted size={11} style={{ textAlign: 'right'}}>Saving</Text>
</Block>
}
{(edited && !saving) &&
<Block style={{flex: 1}}>
<Text muted size={11} style={{textAlign: 'right', color: '#36d79a'}}>saved</Text>
</Block>
}
</>
{!isLoading ? (
<Input
editable={radioCustom}
onChangeText={setInputValue}
onChange={inputChanged}
value={inputValue}
right placeholder="Type your custom question here."
iconContent={<Block />}
/>) : <Text></Text>
}
<Text muted size={12}>Free text | Your employees will see that this is a custom question</Text>
</Block>
When I try to console.log('inputValue: ', inputValue) inside inputChanged function will give missing one character. Example when I try to input Text this will give value in console Tex which missing character t.
Maybe there's a delay on capturing text value input after the onChange function triggers
What's the real culprit of these code?
It is probably the async nature of the setState function
Try to log the input value like this:
useEffect(() => {
console.log(inputValue)
}, [inputValue]) //Only run when inputValue is set
Solve it by assigning the Input defaultValue={inputValue} instead of value={inputValue}

KeyboardAvoidingView keyboardVerticalOffset error in react-native

I used keyboardVerticalOffset to use Static Button in KeyboardAvoidingView of react-native.
but
The red arrow is showing the unexpected behavior.
As you can see extra space is being added but it's a white area.
here is my code.
const Code = () => {
const emailInput = useRef(null);
const birthInput = useRef(null)
const scrollViewRef = useRef(null);
const [email, setEmail] = useState<string>('');
const scrollToEmail = () => {
// I want to scroll the y position of the scroll to the center where the TextInput's cursor is focused.
};
return (
<SafeAreaView style={{ flex: 1}}>
<KeyboardAvoidingView style={{flex: 1}} behavior={'height'} keyboardVerticalOffset={73 + 50}>
<ScrollView keyboardShouldPersistTaps="always" ref={scrollViewRef}>
<InputBox> <== height: 50
<TextInput
ref={emailInput}
onFocus = {() => scrollToEmail()}
/>
</InputBox>
<InputBox> <== height: 50
<TextInput
ref={birthInput}
onFocus = {() => scrollToBirth()}
/>
</InputBox>
</ScrollView>
</KeyboardAvoidingView>
<View style={{position: 'absolute', bottom: 0, width: '100%'}}>
<BottomBtn check={check} onPress={submit} text={`ok`} /> <== height: 73
</View>
</SafeAreaView>
);
};
export default Code;
and <KeyboardAvoidingView behavior={'padding'} keyboardVerticalOffset={73}/> is not working. The keyboard closes immediately.
I would like the space for the Sticky button and TextInput to fit perfectly. What should I do?
I have used KeyboardAvoidingView with height before on a scrollview too but got the same result. My suggestion is to try different behaviour, but I think its better to use react-native-keyboard-aware-scroll-view. It has more properties and its easier to use.

React Native TextInput showing undefined on android

I am using passing function as prop to a component and updating the state of textinput on change.
It is working fine on the web browser and alert the same input written but when I run it on my android device using expo client.
It alerts undefined instead of the actual input.
My piece of code is:
//Main Component
const [search, setsearch] = useState('');
const searchinput = (inp) => {
setsearch(inp.target.value);
alert(search);
};
<Nav Search={searchinput} inpValue={search} Click={() => searchapi()} />
//Nav Component
<TextInput
value={props.inpValue}
onChange={props.Search}
style={{height: 25, width: 100, borderColor: 'white', borderWidth: 0}}
/>
You can do something like this:
const Nav = (props) => {
return (
<TextInput
value={props.inpValue}
onChange={props.Search}
style={{height: 125, width: 100, borderColor: 'black', borderWidth: 1}}
/>
);
};
export default function App() {
const [search, setsearch] = React.useState('');
React.useEffect(() => {
alert(search);
}, [search]);
const searchinput = (inp) => {
setsearch(inp.nativeEvent.text);
};
return <Nav Search={searchinput} inpValue={search} />;
}
Here the current TextInput text value is retrieved inside the onChange using inp.nativeEvent.text.
https://reactnative.dev/docs/textinput.html#onchange
I've also added a useEffect hook that correctly tracks the current value of the search state. With the way you were accessing the search state after updating it, you can't be sure that you're actually referring to current value of the TextInput

react native flat list how to force list items to be the same height?

I have a React-Native application where I am using FlatList to display a list of items obtained from the server. The list has 2 columns and I need my list items to be the same height. I put a border around the code rendering my list items but the list items are not the same height. I have tried using flexbox settings to make the view fill the container, but everything I try makes no difference.
I have created a simplified version of my app to illustrate the issue:
See that the red bordered areas are NOT the same height. I need to get these to be the same height.
The grey border is added in the view wrapping the component responsible for a list item and the red border is the root view of the component responsible for a list item. See the code below for clarity.
I can not use the grey border in my application because my application shows empty boxes whilst the component responsible for a list item is getting additional information from the server before it renders itself
Furthermore I can not used fixed sizes for heights.
Application Project structure and code
My code is split up in a manner where the files ending in "container.js" get the data from the server and pass it to its matching rendering component. For example, "MainListContainer" would be getting the list from the server and then pass the list data to "MainList", and "ListItemContainer" would get additional information about the single list item from the server and pass it to "ListItem" to render the actual item. I have kept this model in my simplified application so its as close to my real application as possible.
index.js
import {AppRegistry} from 'react-native';
import MainListContainer from './app/components/MainListContainer';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => MainListContainer);
MainListContainer.js
import React from 'react';
import MainList from './MainList';
const data = [
{id: '1', title: 'Item 1', subtitle: 'A', description: 'This is the first item.'},
{id: '2', title: 'Item 2', subtitle: 'B', description: 'The Big Brown Fox Jumped over the lazy dogs. The Big Brown Fox Jumped over the lazy dogs.',},
];
const MainListContainer = () => {
return ( <MainList items={data} /> );
};
export default MainListContainer;
MainList.js
import React from 'react';
import {StyleSheet, FlatList, View} from 'react-native';
import ListItemContainer from './ListItemContainer';
export default class MainList extends React.Component {
constructor(props) {
super(props);
this.state = { numColumns: 2};
this.renderItem = this.renderItem.bind(this);
}
renderItem({item, index}) {
return (
<View style={styles.flatListItemContainer}> <!-- THIS IS WHERE THE GREY BORDER IS ADDED -->
<ListItemContainer key={index} item={item} />
</View>
);
}
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
}
};
const styles = StyleSheet.create({
flatListItemContainer: {
flex: 1,
margin: 10,
borderColor: '#ccc',
borderWidth: 1,
},
});
ListItemContainer.js
import React from 'react';
import ListItem from './ListItem';
const ListItemContainer = (props) => {
const { item } = props;
return (
<ListItem item={item} />
);
};
export default ListItemContainer;
ListItem.js
import React from 'react';
import {TouchableHighlight, View, StyleSheet, Image, Text} from 'react-native';
const ListItem = (props) => {
const { item } = props;
return (
<TouchableHighlight
underlayColor="white"
>
<View style={styles.containerView}> <!-- THIS IS WHERE THE RED BORDER IS ADDED -->
<View style={styles.top_row}>
<Image style={styles.image} source={require('../images/placeholder.png')} />
<View style={styles.title_texts}>
<Text style={{fontWeight:'bold'}}>{item.title}</Text>
<Text style={{color: 'rgb(115, 115, 115)'}}>{item.subtitle}</Text>
</View>
</View>
<Text>{item.description}</Text>
</View>
</TouchableHighlight>
);
};
export default ListItem;
const styles = StyleSheet.create({
containerView: {
padding: 14,
borderColor: 'red',
borderWidth: 1,
},
top_row: {
flex: 1,
flexDirection: 'row',
marginBottom: 10,
},
title_texts: {
flex: 1,
flexDirection: 'column',
},
image: {
alignSelf: 'flex-end',
resizeMode: 'cover',
height: 40,
width: 40,
marginRight: 20
},
});
What I have tried
ListItem.js : move the style onto the "TouchableHighlight" view
ListItem.js : add a view wrapping "TouchableHighlight" view and adding style there
ListItem.js : added "alignItems:'stretch' on the "TouchableHighlight, added it to the "containerView" style, tried it on the description field too
same as "alignItems" but used "alignedSelf" instead
same as "alignItems" but used "alignedContent" instead
tried using "flexGrow" on different views (container, description)
You can measure the height of every element in the list and when you determine the maximum height, you can use that height for every element in the list.
const Parent = ({ ...props }) => {
const [maxHeight, setMaxHeight] = useState<number>(0);
const computeMaxHeight = (h: number) => {
if (h > maxHeight) setMaxHeight(h);
}
return (
<FlatList
data={props.data}
renderItem={({ item }) => (
<RenderItem
item={item}
computeHeight={(h) => computeMaxHeight(h)}
height={maxHeight}
/>
)}
....
/>
)
}
The Items:
const RenderItem = ({...props }) => {
return (
<View
style={{ height: props.height }}
onLayout={(event) => props.computeHeight(event.nativeEvent.layout.height)}
>
<Stuffs />
</View>
)
}
This is a very non-performant way of achieving this. I would avoid this if I have a long list or any list of more than a few items. You however can put certain checks in place to limit rerendering etc. Or alternatively if it is only text that will affect the height, then you can only measure the height of the element with the most text and use that element's height for the rest.
Instead of set fixed width height, you can use flex box to achieve it. I just solved the issue by removing alignSelf at the FlatList and add alignItems center on it.
Wrap the flatList in flex box with align item center, you can add the code in your MainList.js file, the first <View>, i.e:
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View style={{flex: 1, alignItems: 'center'>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
If still not reflected, you may try to add flex:1, alignItems center in FlatList style props.
You are missing a very basic concept of giving fixed height to the flatlist items, in your ListItem.js, try to set height:200 in containerView. Let me know if that works for you

How can I display 30 pages of text in a (scrolling) screen

I want to display 30 pages of text on a screen. I've tried ScrollView and FlatList but I get a white screen. Only when I try with ScrollView to display only 2 pages, works fine.
I do not want to use a WebView, because I would like to have all data in the app (no internet connection needed).
Here is what I've already tried:
With FlatList:
I have a text.js as a model, which I use to create a Text Object in an array, which I then use as data for the FlatList. For the renderItem function (of FlatList) I use a TextItem to display the text.
text.js
function Text(info) {
this.id = info.id;
this.text = info.text;
}
export default Text;
LongTextModule.js
import Text from '../../models/text';
export const LONGTEXT = [
new Text({
id:'text_1',
text:`.....longtext....`
})
]
TextItem.js
const TextItem = (props) => {
return (
<View style={styles.screen} >
<Text style={styles.textStyle}>{props.longText}</Text>
</View >
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
},
textStyle: {
justifyContent: 'flex-start',
alignItems: 'flex-start',
fontFamily: 'GFSNeohellenic-Regular',
fontSize: 20,
padding: 10,
}
});
TextDetailScreen.js
const TextDetailScreen = (props) => {
const renderText = data => {
return <TextItem longText={data.item.text} />
}
return <FlatList
data={LONGTEXT}
keyExtractor={(item, index) => item.id}
renderItem={renderText}
/>
};
I think it's needless to show the code with ScrollView, since ScrollView is only for a small list.
I even tried to render the longText like this in the screen.
Without the ScrollView I get the first portion, but with ScrollView a white screen.
const TextDetailScreen = (props) => {
return (
<ScrollView>
<Text> ...longText...</Text>
</ScrollView>
);
};
I'm sure there is a way to display a lot of pages of text on a screen?
But how?
Thank you :)
It seems not to be an unknown Issue, I've also read from time to time about this issue.
But not to use Webview, because you wan't to have all Data in your app - don't have to be an Argument against Webview. With WebView, you also can display Data from your App-Storage.
Example:
<WebView style={styles.myStyle} source={{html: `<p style="font-size:48px">${longtext}</p>`}} />