callback scrollTo in a ScrollView React Native - react-native

I would like to call a function at the end of the function scrollTo called like that :
scrollTo({y: 0, animated: true})
but by default this function doesn't have a second parameter.
So how can i handle the end of the scroll animation to trigger an other function ?

You can use onMomentumScrollEnd as mentioned in this issue
However if you want more control over your scroll state you can implement smth like this
import React from 'react';
import { StyleSheet, Text, View, ScrollView, Button } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<ScrollView
style={{ marginVertical: 100 }}
ref={this.refScrollView}
onScroll={this.onScroll}
>
<Text style={{ fontSize: 20 }}>
A lot of text here...
</Text>
</ScrollView>
<Button title="Scroll Text" onPress={this.scroll} />
</View>
);
}
componentDidMount() {
this.scrollY = 0;
}
onScroll = ({ nativeEvent }) => {
const { contentOffset } = nativeEvent;
this.scrollY = contentOffset.y;
if (contentOffset.y === this.onScrollEndCallbackTargetOffset) {
this.onScrollEnd()
}
}
onScrollEnd = () => {
alert('Text was scrolled')
}
refScrollView = (scrollView) => {
this.scrollView = scrollView;
}
scroll = () => {
const newScrollY = this.scrollY + 100;
this.scrollView.scrollTo({ y: newScrollY, animated: true });
this.onScrollEndCallbackTargetOffset = newScrollY;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20,
},
});

Related

I want to make a bottom bar The bottom bar goes to the page I want when I click it

enter image description here
I want to make a bottom bar The bottom bar goes to the page I want when I click it
I want to make it similar to that picture
I want to make a bottom bar like that
I've been googling hard, but I can't find anything similar to me
https://reactnavigation.org/docs/bottom-tab-navigator/
I want to use the code in the address over there, but I don't know
import React, { useRef, useState, useCallback, useEffect } from
'react';
import {
View,
BackHandler,
Platform,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { WebView } from 'react-native-webview';
import { Image } from "react-native";
const DELAY_BEFORE_WEBVIEW = 10; // <--- seconds before webview load
export default function App() {
// ref
const webView = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webView.current) {
webView.current.goBack();
return true;
}
return false;
}, [canGoBack]);
// effects
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBack);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBack);
};
}, [handleBack]);
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 1000 * DELAY_BEFORE_WEBVIEW);
}, []);
// states
const [isLoading, setIsLoading] = useState(true);
return (
<View style={styles.container}>
<WebView
ref={webView}
source={{ uri: 'https://www.talesrunnerbestguild.co.kr/' }}
style={styles.webView}
onLoadProgress={(event) =>
setCanGoBack(event.nativeEvent.canGoBack)}
/>
{isLoading && <CenterLoader />}
</View>
);
}
const CenterLoader = () => (
<View style={styles.loaderContainer}>
<Imagesource{require('/workspace/talesrunner23/assets/js34.png/')}
style={{height:115,width:90}}/>
</View>
);
const styles = StyleSheet.create({
container: { flex: 1 },
loaderContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor:'white' // <-- comment this to show webview while
loading
},
webView:
Platform.OS === 'ios'
? { marginTop: 30, marginBottom: 40 }
: { marginTop: 30 },
});

How to re-render after state change in React-Native?

I am developing React Native app, where must be overlay view before starting. Like "welcome screen". After you have read content in overlay view, you press ok.
setState is not working. Throw error TypeError: _this.setState is not a function
I'll tried to use setState like this:
removeElement = () => {
//this.state.loadingScreen = false;
this.setState({
loadingScreen: false
})
console.log(`Staten arvo nyt: ` + this.state.loadingScreen);
When I use setState it should re-render component, but I don't know what is the reason why it not works. I can change state this.state.loadingScreen = false;, but it is not re-rendering. And forceUpdate() not working either.
QUESTION: How can I render component again to get rid off overlay view?
My code:
import React, { useState, setState, Component } from "react";
import { Text, View, StyleSheet, TextInput, Button, Alert, TouchableOpacity} from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
state = {
loadingScreen: true,
}
let userInput = "";
let userInputName = "";
readUserInput = (text) => {
userInput = text
}
readUserInputName = (text) => {
userInputName = text
}
checkUserInput = () => {
if(userInput.length < 1 && userInputName.length < 1){
Alert.alert("Tarkista rekisterinumero ja nimi")
}
else{
storeData = async () => {
try{
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
}
catch(e){
console.log(e);
}
}
storeData();
navigation.navigate("GeneralInspection")
}
}
renderElement = () =>{
if(this.state.loadingScreen == true)
return <View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{fontSize:20}}>Change state to false</Text>
</TouchableOpacity>
</View>;;
return null;
}
removeElement = () => {
this.state.loadingScreen = false
}
setTimeout(
function() {
}
.bind(this),
1000
);
return (
<View style={styles.view}>
{ this.renderElement() }
<Text style={styles.text}>Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{fontSize:20}}>Aloita</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
view: {
height: "100%",
width: "100%",
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 40,
fontWeight: "bold",
padding:10,
fontStyle:"italic"
},
inputStyle: {
height:50,
borderColor:"black",
borderWidth:1,
width:"50%",
marginBottom:15,
textAlign:"center",
fontSize: 20,
borderRadius:5,
},
button: {
backgroundColor:"#007bff",
borderRadius:5,
padding:8,
width:"50%",
height: 60,
alignItems:"center",
justifyContent: "center",
},
fullScreen: {
height: "100%",
width: "100%",
zIndex: 100,
position: "absolute",
backgroundColor: "red",
}
});
You are using a functional component in the OP, which requires you to use useState Hooks for managing your state.
If you were using a class component, you could use the setState approach. In both cases, React will take care of the re-rendering part.
If using class-based approach, mutating state like this.state.loadingScreen = false will not trigger a re-render of the UI. You will have to use the setState method instead.
Class-based approach
import React from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default class StartPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loadingScreen: true,
userInput: "",
userInputName: ""
};
}
readUserInput = (text) => {
this.setState({
userInput: text
});
}
readUserInputName = (text) => {
this.setState({
userInputName: text
});
}
storeData = async () => {
const { userInput, userInputName } = this.state;
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
this.props.navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
}
checkUserInput = () => {
const { userInput, userInputName } = this.state;
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
this.storeData();
}
renderElement = () => {
if (this.state.loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
}
removeElement = () => {
this.setState({
loadingScreen: false
});
}
render() {
return (
<View style={styles.view} >
{ this.renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={this.state.userInputName}
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={this.state.userInput}
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
}
Functional component-based approach
import React, { useState } from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
const [loadingScreen, setLoadingScreen] = useState(true);
const [userInput, setUserInput] = useState('');
const [userInputName, setUserInputName] = useState('');
const readUserInput = (text) => {
setUserInput(text);
};
const readUserInputName = (text) => {
setUserInputName(text);
};
const storeData = async () => {
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
};
const checkUserInput = () => {
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
storeData();
};
const renderElement = () => {
if (loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
};
const removeElement = () => {
setLoadingScreen(false);
};
return (
<View style={styles.view} >
{renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={userInputName}
onChangeText={readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={userInput}
onChangeText={readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}

Whats the best way to change the background color of collapse-accordion dynamically in react native

i have used accordion-collapse-react-native in my project .But i tried to change each collapse color dynamically,which is based on the Qr scanning. i failed to display the color for each collapse.can you help me out?
You can change the background color by using this.setState
Set the initial background color in your constructor
this.state = {
backgroundColor: randomHex()
}
in your render function change your style prop to:
[styles.container, {backgroundColor: this.state.backgroundColor}]
and onClick add
this.setState({backgroundColor: randomHex()});
Heres the full code
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
let randomHex = () => {
let letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
export default class randomBackground extends Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this);
this.state = {
backgroundColor: randomHex()
};
}
onClick() {
console.log('clicked ');
this.setState({backgroundColor: randomHex()});
}
render() {
return (
<TouchableHighlight onPress={ this.onClick } style={[styles.container, {backgroundColor: this.state.backgroundColor}]}>
<View>
<Text style={styles.instructions}>
Tap to change the background
</Text>
</View>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: randomHex()
},
instructions: {
color: "white"
}
});
AppRegistry.registerComponent('randomBackground', () => randomBackground);

How to trigeer two function with one react component?

I create a class component with animation for common button:
import React, { Component } from 'react';
import { Text, TouchableOpacity, StyleSheet, Animated, Easing, View } from 'react-native';
class AnimatedPrimaryButton extends Component {
constructor(props) {
super(props);
this.state = {
toggle: false,
animated: new Animated.Value(0)
}
}
animatedButton = (toggle) => {
this.state.animated.setValue(toggle ? 1 : 0);
Animated.timing(this.state.animated, {
toValue: toggle ? 0 : 1,
duration: 250,
easing: Easing.bounce
}).start();
this.setState({ toggle: !toggle });
}
render() {
const { toggle, animated } = this.state;
const { onPress, disabled, width, height } = this.props;
return (
<TouchableOpacity
disabled={disabled}
onPress={onPress}
style={[styles.buttonStyle, { width, height }]}
>
<Animated.View style={{
// other styles
transform: [{ scale: animated.interpolate({ inputRange: [0, 1], outputRange: [0, 1]})
}
]
}}>
</Animated.View>
<Text style={[styles.textStyle, { fontSize }]}>
{children}
</Text>
</TouchableOpacity>
);
};
}
const styles = StyleSheet.create({
// some styles
});
export default AnimatedPrimaryButton;
I use the create component on other screen like:
import AnimatedPrimaryButton from '../Shared/Button/AnimatedPrimaryButton';
doSomething = () => {
// do something...
}
render() {
return (
<View>
<AnimatedPrimaryButton
onPress={() => this.doSomething()}
width={400}
height={57}
fontSize={20}
backgroundColor={confirmButtonBg}
disabled={disabledConfirmButton}
>
{I18n.t('SIGN_IN_BUTTON')}
</AnimatedPrimaryButton>
</View>
);
}
Now I want to use doSomething function and trigger animatedButton at the same time.
In some conditions my disable will switch true or false, so I try to set the code on my AnimatedPrimaryButton is not working.
onPress={() => !disabled ? this.animatedButton(toggle) : onPress}
It looks like use the props onPress under arrow function won't work.
How to use doSomething and animatedButton function on class component AnimatedPrimaryButton ?
Any help would be appreciated.
in AnimatedPrimaryButton component you can make a onPress function
onPress(){
this.props.onPress();
this. animatedButton();
}
and rest you are sending the doSomething() function correctly on onPress while calling AnimatedPrimaryButton on other screen.

react native ref not working in dynamic adding rows

I am creating spreadsheet in react native and i want to append columns if we click on add button, but when i used ref in dynamic cols it gives error. here is my code .. please tell me how i can use ref so it will not give me error. following is the error that i receive when i use ref ..
import React, { Component } from 'react';
import {
StyleSheet,
View,
TouchableOpacity,
Text,
TextInput,
ScrollView
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
var unique=1;
export default class Sheet extends Component {
constructor(props) {
super(props);
this.state = {
currentRowNo:1,
serialNo:1,
rows:this.props.species.length,
breaker:true,
appendRows:[],
index:1
}
}
rendercol(rowNo)
{
const col=[];
var serialNo=this.state.index;
col.push(<View key="rowNum" style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>{rowNo}</Text></View></View>);
for (var j=serialNo;j<=this.state.rows+serialNo-1;j++)
{
col.push(<TouchableOpacity style={styles.fixCellWidth} key={"key_"+j}><Text style={{fontSize:16}} onPress={this.changeValue.bind(this,j)} ref={"red"+j}>{console.log(this.props.action.bind(this,j))}</Text></TouchableOpacity>);
}
return <View key={"Row"+rowNo}>{col}</View>;
}
changeValue(val)
{
this.props.changeVal(val);
}
addRow(){
this.state.appendRows.push(
this.rendercol(this.state.index)
)
this.setState({
index: this.state.index + 1,
appendRows: this.state.appendRows
})
}
render()
{
var _scrollView: ScrollView;
const row=[];
const sheet=[];
sheet.push(<View key="headRow" style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>S./#</Text></View></View>);
this.props.species.map((option) => {
sheet.push(<View key={option.code} style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>{option.code}</Text></View></View>);
});
sheet.push(<TouchableOpacity key="rowAdd" style={styles.column} onPress={this.addRow.bind(this)}><View style={[styles.fixCellWidth,{backgroundColor:"green"}]}><Icon name="plus" style={[styles.icon,styles.fontWhite]}/></View></TouchableOpacity>);
return (
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }}
automaticallyAdjustContentInsets={false}
horizontal={false}
style={{height:650,paddingBottom:10}}>
<View style={styles.column}>
<View>{sheet}</View>
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }}
automaticallyAdjustContentInsets={false}
horizontal={true}
style={[styles.scrollFull]}>
{this.state.appendRows}
</ScrollView>
</View>
</ScrollView>
);
}
}
You are repeating ref assignation in your render function:
return (
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }} // <- here
automaticallyAdjustContentInsets={false}
horizontal={false}
style={{height:650,paddingBottom:10}}>
<View style={styles.column}>
<View>{sheet}</View>
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }} // <- here
automaticallyAdjustContentInsets={false}
horizontal={true}
style={[styles.scrollFull]}>
{this.state.appendRows}
</ScrollView>
</View>
</ScrollView>
);
But that is not the problem. The problem is that you are creating refs outside render method of the component, to be specific in your rendercol function. Here is the error explanation: https://gist.github.com/jimfb/4faa6cbfb1ef476bd105.
You can add rows using state, look at this approach: Append TextInput component one after another when press button in React Native
import React, { Component } from 'react';
import { View, Text, Platform, StyleSheet, TouchableOpacity,TouchableHighlight, Animated, ScrollView, Image } from 'react-native';
let index = 0
export default class App extends Component {
constructor(context) {
super(context);
this.state = {
rows: []
}
}
_addRow(){
this.state.rows.push(index++)
this.setState({ rows: this.state.rows })
}
render() {
let CheckIndex = i => {
if((i % 2) == 0) {
return styles.gray
}
}
let rows = this.state.rows.map((r, i) => {
return <View key={ i } style={[styles.row, CheckIndex(i)]}>
<Text >Row { r }, Index { i }</Text>
</View>
})
return (
<View style={styles.container}>
<TouchableHighlight onPress={ () => this._addRow() } style={styles.button}>
<Text>Add new row</Text>
</TouchableHighlight>
{ rows }
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 60,
},
gray: {
backgroundColor: '#efefef'
},
row: {
height:40,
alignItems: 'center',
justifyContent: 'center',
borderBottomColor: '#ededed',
borderBottomWidth: 1
},
button: {
alignItems: 'center',
justifyContent: 'center',
height:55,
backgroundColor: '#ededed',
marginBottom:10
}
});