TextInput goes Missing, but Keyboard Stays After Every KeyStroke? - reactjs

Trying to figure out an issue with the TextInput component I have which is acting weird. Basically, after every keystroke, the textinput seems to be losing its focus, but the keyboard seems to stay..
Im implementing it as a search bar that is triggered / animated when a search icon is touched by the user:
<TouchableHighlight
activeOpacity={1}
underlayColor={"#ccd0d5"}
onPress={onFocus}
style={styles.search_icon_box}
>
onFocus method:
const onFocus = () => {
setIsFocused(true);
const input_box_translate_x_config = {
duration: 200,
toValue: 0,
easing: EasingNode.inOut(EasingNode.ease)
}
const back_button_opacity_config = {
duration: 200,
toValue: 1,
easing: EasingNode.inOut(EasingNode.ease)
}
// RUN ANIMATION
timing(input_box_translate_x, input_box_translate_x_config).start();
timing(back_button_opacity, back_button_opacity_config).start();
ref_input.current.focus();
}
just a simple animation where when triggered the search bar will slide from the right side of the screen
<Animated.View
style={[ styles.input_box, {transform: [{translateX: input_box_translate_x}] } ]}
>
<Animated.View style={{opacity: back_button_opacity}}>
<TouchableHighlight
activeOpacity={1}
underlayColor={"#ccd0d5"}
onPress={onBlur}
style={styles.back_icon_box}
>
<MaterialIcons name="arrow-back-ios" size={30} color="white" />
</TouchableHighlight>
</Animated.View>
<SearchBar
placeholder='Search'
keyboardType='decimal-pad'
returnKeyType='done'
ref={ref_input}
value={searchText}
onChangeText={search}
onClear={onBlur}
onSubmitEditing={onBlur}
onFocus={() =>console.log("focus received" ) }
onBlur={() => console.log("focus lost") }
/>
</Animated.View>
Search
const search = (searchText) => {
setSearchText(searchText);
let filteredData = AnimalList.filter(function (item) {
return item.tag_number.toString().includes(searchText);
});
setFilteredData(filteredData);
}
So, when I clicked on the search icon, the search bar will present itself through animated view and the keyboard will automatically be focused. However, after entering a single character on the keyboard the searchbar just vanishes with keyboard still showing.
I tried to debug using onFocus={() =>console.log("focus received" ) } and it looks like the searchBar is still focused on, its just not showing
EDIT: Issue Video Here https://github.com/renwid/test/issues/1

You can show the full version of SearchBar to get more help
[Updated]
The initial step, Animated.Value must be
const input_box_translate_x = useRef(new Value(width)).current;
const back_button_opacity = useRef(new Value(0)).current;
instead of
const input_box_translate_x = new Value(width);
const back_button_opacity = new Value(0);
Because you using the function component and re-render might be re-create the component and the Animated.Value will be re-create too. So the Animated.Value can not keep the state and cause you issue

Related

Passing a button select choice back to previous screen

So after some research, I have learned how to make a button that will take the user to another screen, and provide them a text input where they can enter some words, then on pushing the done button take them back to the previous screen where what they typed will be displayed. But for my particular needs, I am trying to figure out how to instead of a text input have a selection of buttons, such as "large, medium, small" and have that button select the data that would be displayed instead, and return them to the previous page where it is displayed.
initial screen
function HomeScreen( route ) {
navigation = useNavigation();
React.useEffect(() => {
if (route.params?.post) {
}
}, [route.params?.post]);
return (
<View>
<Pressable
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</Pressable
</View>
);
}
button selection screen
function CreatePostScreen( route ) {
const navigation = useNavigation();
const [postText, setPostText] = React.useState('');
return (
<>
<Pressable
title="Done"
onPress={() => {
navigation.navigate({
name: 'Home',
params: { postText },
merge: true,
});
}}
>
<Text>
Large
</Text>
</Pressable>
</>
);
}
any insight is greatly appreciated.
You can pass the data in form of object
{post: postText,
buttonType: 'medium'}
For getting the data
React.useEffect(() => {
if (route.params?.post) {
var buttonType= route.params?.buttonType
}
}, [route.params?.post]);
You can store the button type in a variable or state
var buttonType = route.params?.buttonType
Or You can try with useState() hooks
const [buttonType, setButtonType]=useState("")
setButtonType(route.params?.buttonType)
The for using it just do the following
<Text>{buttonType}</Text>
Please follow the React-Documentation

Scroll down to a specific View in React Native ScrollView

I want to be able to scroll a after pressing button so that it visible on screen. How can I tell a react-native ScrollView move to a certain?
Hello you can use the property scrollTo like below
import {useRef} from "react"
import {
ScrollView,
Button,
} from 'react-native';
const YouComp = () => {
const refScrollView = useRef(null);
const moveTo = () => {
refScrollView.current.scrollTo({x: value1, y: value2});
// or just refScrollView.current.scrollTo({x: value1}); if you want to scroll horizontally
// or just refScrollView.current.scrollTo({y: value2}); if you want to scroll vertically
}
return (<>
<Button onPress={moveTo} title="title" />
<ScrollView ref={refScrollView}>
...
</ScrollView>
</>);
}
You can set whether x or y value or both
Check the full doc here
First you need to create reference to element
this.scrollViewRefName = React.createRef()
Then pass it to ref attribute
<ScrollView ref={this.scrollViewRefName}>
Then you trigger the function from your button with scrollToTheEnd or wherever you want to scroll within the element
<View style={styles.ButtonContainer}>
<Button onPress={() => { this.scrollViewRef.current.scrollToTheEnd }} />
</View>
Note that you may need extra callback function in onPress depending on from which context you have the components
using ref and scrollTo is just bullshit and dose not always work.
Here is how i did it.
const [scrollYPosition, setScrollYPosition] = useState(0);
const [data, setData] = useState([]);
const goToItem = () => {
// lets go to item 200
// the 200 is the item position and the 150 is the item height.
setScrollYPosition(200 * 150);
}
<ScrollView contentOffset = {
{
y: scrollYPosition,
x: 0
}
}>
// papulate your data and lets say that each item has 150 in height
</ScrollView>

How to apply animation in react-native?

I want to add animation to this search bar.
when the user touches the search bar it size decreases and again increases and gets to its default size(like animation in popups)
This is my code
<View style={{flexDirection:'row',alignSelf:'center'}}>
<TextInput
onChangeText={(text) => setSearch(text)}
onFocus={()=>{
setSize('92%');
setInterval(()=>{setSize('95%')},1000)
}}
placeholder="Search"
style={{...styles.searchbox,width:size}}
></TextInput>
</View>
I am currently trying to change width..
Firstly, I suggest you to take a look at RN animated documentation, maybe it will help you to understand better how the animations work.
Also, it depends on what you're having there: a class component or a function component.
If you're using a function component, you could do it like this:
Creating a custom hook, called, let's say useAnimation(), which would look something like this
export const useAnimation = ({ doAnimation, duration, initialValue, finalValue }) => {
const [animation, setAnimation] = useState(new Animated.Value(initialValue))
useEffect(() => {
Animated.spring(animation, {
toValue: doAnimation ? initialValue : finalValue,
duration,
bounciness: 8,
useNativeDriver: false
}).start();
}, [doAnimation]);
return animation
}
As it is said in the documentation, you could animate only Animated components, and for example if you want to have an animated View, the tag will be <Animated.View> {...} </Animated.View, but for the <TextInput> we have to create the animated component:
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
and combining the first 2 steps
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
const [collapsed, setCollapsed] = useState(true)
const animation = useAnimation({ doAnimation: collapsed, duration: 300, initialValue: 20, finalValue: 200 });
const onFocusText = () => setWidth(false)
return (
<AnimatedTextInput
onFocus={onFocusText}
placeholder={"Search something"}
style={{width: animation, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 4, padding: 10}}
/>
)
Also, if you're having a class component, you could have a method which will start the animation (but don't forget about the step 2 which is essential)
private size: Animated.Value = new Animated.Value(COLLAPSED_VALUE)
get resizeInputWidth(): Animated.CompositeAnimation {
return Animated.timing(this.size, {
toValue: EXPANDED_VALUE,
duration: 500,
})
}
startAnimation = () => this.resizeInputWidth.start()
<AnimatedTextInput
onFocus={this.startAnimation}
style={{ width: this.size }}
/>

How do I stop React Native Maps' Draggable Marker from disabling touches outside the MapView on iOS using Google as the provider?

On iOS with Google provider React Native Maps' Draggable Marker disables touches outside the MapView until it registers a touch inside the MapView. On Android everything is fine, but for some reason on iOS when I finish dragging a marker on the map and onDragEnd is called, no touch events are registered unless they are on TextInputs. Occasionally a TouchableOpacity will flash momentarily, but it's onPress function is never called. However if I touch inside the MapView, even nowhere near the marker, everything goes back to the way it's supposed to be. It's like react native maps has some finishing event that doesn't occur that forces the focus to stay on the map.
There are a couple tricky things going on but I don't think they're the culprits:
I use setState with onDragStart and onDragEnd to disable the ScrollView parent component, otherwise the dragging gets interrupted by the scroll on Android.
As part of onDragEnd I make a callout with react-native-geocoding, then update the region state. I have commented all this out and it still doesn't work.
The plan is to animate to the new region after the state is updated, but until this is resolved there's no point. Here's my code, or what's left of it after taking out all the commented stuff:
const MapSection = (props) => {
const {
location, setIsDraggingMarker, onDragMarkerEnd, region,
} = props;
const [isLoading, setIsLoading] = useState(false);
const onDragEnd = (e) => {
onDragMarkerEnd(e.nativeEvent.coordinate);
};
if (!isEmpty(location)) {
return (
<View style={styles.mapContainer}>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.mapView}
initialRegion={region}
scrollEnabled={false}
zoomEnabled={false}
rotateEnabled={false}
>
<Marker
draggable
onDragStart={() => setIsDraggingMarker(true)}
onDragEnd={onDragEnd}
coordinate={region}
>
<Image
style={styles.imageStyle}
resizeMode="stretch"
source={require('../../../../assets/images/draggableMarkerPin.png')}
/>
<Callout
style={styles.customCallout}
onPress={() => { }}
>
<View style={styles.callout}>
<Feather
name="move"
size={12}
color="black"
/>
<Text style={{
paddingLeft: 4,
fontSize: 14,
}}
>
Press and drag to fine tune location!
</Text>
</View>
</Callout>
</Marker>
</MapView>
</View>
);
}
return null;
};
export default MapSection;
//FROM THE PARENT COMPONENT, PASSED AS PROPS
const onDragMarkerEnd = (coords) => {
setIsDraggingMarker(false);
setRegionWithLatLng(coords.latitude, coords.longitude);
};
const setRegionWithLatLng = async (latitude, longitude) => {
const fullLocationData = await geocodeLocationByCoords(latitude, longitude);
const currentLocation = { ...fullLocationData };
const newRegion = {
...region,
latitude: fullLocationData.geometry.location.lat,
longitude: fullLocationData.geometry.location.lng,
};
setRegion(newRegion);
dispatchEventDetailsState({
type: FORM_INPUT_UPDATE, value: currentLocation, isValid: true, input: 'location',
});
};
If there's any way to just trick the MapView into thinking it's been pressed (I'ved tried referencing the MapView and calling mapView.current.props.onPress(), no dice), then I'm fine with that. Just any workaround.

TextInput /Input element looses focus on key press

I'm currently developing an application using React Native.
This trial app has a component that has a TextInput and two buttons (ADD and DELETE).
When I press the ADD Button, a new component appears. If I press the DELETE Button that the same component disappears.
The screen is like the photo bellow:
I control the TextInput with the index which is the same number as the index of the component.
My question is: why can't I enter some text as usual in this code?
I have to focus the cursor every time I enter 1 word.
I lose a flashing vertical bar(I check in the photo below) in the input area every time I press a key.
How can I resolve this problem?
And, I want to control the inputted value from TextInput with array[] not object{} because in case of an array is easier to delete a component sliding index and value like an explanation below:
I have no idea to control index and value with an, object and it's complicated for my skill now, but if there are some nice ways to resolve using object, I hope to know it.
Here is the code:
import React, { useState } from "react";
import { View, Text, Button, TextInput, StyleSheet } from "react-native";
function Item({ number, handleInput, handleAdd, handleDelete, index }) {
return (
<View style={styles.list}>
<Text>{index}</Text>
<TextInput
style={{ borderWidth: 1 }}
value={number[index]}
onChange={(e) => {
handleInput(index, e.nativeEvent.text);
}}
></TextInput>
<Button
title="ADD"
onPress={() => {
handleAdd();
}}
/>
<Button
title="DELETE"
onPress={() => {
handleDelete(index);
}}
/>
</View>
);
}
export default function TestStateArray() {
const [count, setCount] = useState(1);
const [number, setNumber] = useState([]);
function handleAdd() {
setCount((v) => v + 1);
}
function handleDelete(index) {
setCount((v) => v - 1);
setNumber((v) => {
const ret = v.slice();
ret.splice(index, 1);
return ret;
});
}
function handleInput(index, text) {
setNumber((v) => {
const ret = v.slice();
ret[index] = text;
return ret;
});
}
return (
<View>
{Array.from({ length: count }, (_, i) => (
<Item
number={number}
handleInput={handleInput}
handleAdd={handleAdd}
handleDelete={handleDelete}
key={i + "-" + number}
index={i}
/>
))}
</View>
);
}
const styles = StyleSheet.create({
list: {
margin: 10,
padding: 10,
backgroundColor: "#ddd",
},
});
After I have some answer and some comment, I tried changing the code like bellow, but it still has the same problem...
// onChange={(e) => {
// handleInput(index, e.nativeEvent.text);
onChangeText={(text) => {
handleInput(index, text);
}}
node : 12.18.3
react native : 4.10.1
expo : 3.22.3
The above issue occurred because your handle change function is wrong.
Please change...
const [number, setNumber] = useState({}); // change array to Object in useState.
Replace your handler with below function:
function handleInput(index, text) {
setNumber({ ...number, index: text });
}
React Native has onChangeText event on TextInput component can you try that one?

Resources