Scroll down to a specific View in React Native ScrollView - reactjs

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>

Related

TextInput goes Missing, but Keyboard Stays After Every KeyStroke?

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

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?

How to delete item from an array by onPress event in react

I'am making a todo app. I want to call a function on a button Press event and want to pass item id to it.
import React ,{useState} from 'react';
import { StyleSheet, Text, View, TextInput, Button, ScrollView, Alert, FlatList} from 'react-native';
export default function App() {
const [goal,setgoal] = useState('');
const [addInput, setInput] = useState([]);
const changetext= ()=>{
setInput(addInput=>[...addInput,{id: Math.random().toString(), value: goal}]);
};
const texthandler= (enteredText)=>{
setgoal(enteredText);
};
const deleteText = (e)=>{
setInput((addInput)=>addInput.filter(todo=>todo.id !=e.target.id))
}
return (
<View style={styles.screen} >
<View style={styles.InputView}>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}
style={styles.TextInputStyle}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View style={styles.recordList}>
<Text style={styles.Textoutput} id={itemData.item.id} onPress={deleteText}>{itemData.item.value}</Text>
<Button title="Delete" style={styles.DeleteButton} value={itemData} onPress={deleteText} ></Button>
</View>)}/>
</View>
);
}
here i want Delete button to remove respective element from 'addInput' list. same thing is happening by pressing Text field itself.
but here i can pass id from text field but not from button. why so?
how to get it done by using button.
also, should i use 'this' keyword? can we do it without it, because some time it looks confusing to me at initial stage.
thanks in advance
Try this:
const deleteText = (itemID)=>{
setInput(()=>addInput.filter(todo=>todo.id !=itemID))
}
return (
<View style={{marginTop:30}}>
<View>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View>
<Text id={itemData.item.id} onPress={()=>deleteText(itemData.item.id)} >{itemData.item.value}</Text>
<Button title="Delete" value={itemData} onPress={()=>deleteText(itemData.item.id)} ></Button>
</View>)}/>
</View>
Well i am not sure about the answer in terms of React-native but in React i would try something like this:-
onPress= { this.deleteText.bind(this,idx) }
If you are not passing this function as a prop, simply remove bind and call it using this.deleteText(idx)
In react/react-native, you can make a event handler method and can handle array items for example like this:
handleOnPress = value => {
let {mArray} = this.state
if(mArray.includes(value)){
let index = mArray.findIndex((item => item==value))
mArray.splice(index, 1)
}else mArray.push(value)
this.setState({mArray})
}
In Your case, have to just delete item from the array so you can just use splice method. for example:
handleOnDelete = item => {
let {mArray} = this.state;
let index = mArray.findIndex((item => item==value))
if(index > -1) mArray.splice(index, 1)
else console.log('item not found')
}
const deleteText = e => setInput(
addInput => addInput.split(addInput.findIndex(todo => todo.id == e.target.id), 1)
)

React native infinity view pager

I need to create infinity view pager to display calendar days, and add an ability to user for swapping left/right and changing date.
As I see in the documentation, the view pager will work only with preset number of views, and also research some opensource packages - cant find anything about that.
So my question - how can I implement infinity swiping for calendar (or is it possible at all)?
I have an infinite viewpager made with VirtualizedList. It works on iOS an Android.
import React, { Component } from 'react'
import { View, Text, Dimensions, VirtualizedList } from 'react-native'
const { width, height } = Dimensions.get('window')
const startAtIndex = 5000
const stupidList = new Array(startAtIndex * 2)
class InfiniteViewPager extends Component {
//only use if you want current page
_onScrollEnd(e) {
// const contentOffset = e.nativeEvent.contentOffset
// const viewSize = e.nativeEvent.layoutMeasurement
// // Divide the horizontal offset by the width of the view to see which page is visible
// const pageNum = Math.floor(contentOffset.x / viewSize.width)
}
_renderPage(info) {
const { index } = info
return (
<View style={{ width, height }}>
<Text>
{' '}{`index:${index}`}{' '}
</Text>
</View>
)
}
render() {
return (
<VirtualizedList
horizontal
pagingEnabled
initialNumToRender={3}
getItemCount={data => data.length}
data={stupidList}
initialScrollIndex={startAtIndex}
keyExtractor={(item, index) => index}
getItemLayout={(data, index) => ({
length: width,
offset: width * index,
index,
})}
maxToRenderPerBatch={1}
windowSize={1}
getItem={(data, index) => ({ index })}
renderItem={this._renderPage}
onMomentumScrollEnd={this._onScrollEnd}
/>
)
}
}

Resources