React native, how to check arrays are equal or not - arrays

I have created pin code lock screen using react native.
you can see the code with emulator here - https://snack.expo.dev/#codewithbanchi/pincode
default pin is 1234 and it is assigned to an array.
Then I take the inputs from user and assign it to an another array.
Then I wrote this for checking if the user entered pin is equal to the default pin.
if(passcode===defaultCode){
alert("Login sucssesfull");
}else{
alert("Login failed");
}
but each time user touched on a button, alert comes up "Lofin failed''
What went wrong here
I am defining my complete code here and also You can go to above expo link to watch it with the emulator.
import React from "react";
import { Alert, StyleSheet, Text, Touchable, TouchableHighlight, TouchableOpacity,useState, View } from "react-native";
import Loading from './Loading';
const Buttons = () => {
const defaultCode = React.useState(['1','2','3','4'])
const [passcode , setPasscode] = React.useState(['','','',''])
const presControl = num =>{
let tempCode = [...passcode];
for(var i = 0; i<tempCode.length;i++){
if(tempCode[i] == ''){
tempCode[i] = num;
break;
}else{
continue;
}
}
setPasscode(tempCode);
}
if(passcode===defaultCode){
alert("Login sucssesfull");
}else{
alert("Login failed");
}
const clearPress = () =>{
let tempCode = [...passcode]
for(var i = tempCode.length-1;i>=0;i--){
if(tempCode[i] != ''){
tempCode[i]='';
break;
}else{
continue;
}
}
setPasscode(tempCode);
};
let nopad = [
{id:1},
{id:2},
{id:3},
{id:4},
{id:5},
{id:6},
{id:7},
{id:8},
{id:9},
{id:0}
];
return(
<View >
<View style={styles.boxcontaner} >
{passcode.map(p =>{
return <View style={p !=''? styles.box2:styles.box1}></View>
})}
</View>
<View style={styles.noBox}>
{nopad.map(num =>{
return(
<TouchableOpacity style={styles.box}
key={num.id}
onPress={()=>presControl(num.id)} >
<Text style={styles.title}>{num.id}</Text>
</TouchableOpacity>
);
})}
</View>
<TouchableOpacity onPress={clearPress} style={styles.deleter} >
<Text style={styles.title}> x
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
title: {
fontSize:40,
justifyContent:'center',
marginTop:5,
},
box1: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray'
},
box2: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray',
backgroundColor:'red'
},
box: {
width:70,
height:70,
borderRadius:70,
borderWidth:1,
borderColor:'green',
alignItems:'center',
backgroundColor:'#F2F3F4',
justifyContent:'center',
},
boxcontaner:{
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between',
marginLeft:40,
marginRight:40,
marginTop:10,
},
noBox:{
alignItems:'center',
justifyContent:'center',
marginTop:100,
flexDirection:'row',
flexWrap:'wrap',
marginLeft:20,
width:270,
height:200,
},
deleter:{
width:70,
height:70,
borderRadius:70,
borderWidth:1,
borderColor:'green',
alignItems:'center',
backgroundColor:'#F2F3F4',
marginLeft:190,
marginTop:10
}
});
export default Buttons;

arrays are reference types , read this link please - https://masteringjs.io/tutorials/fundamentals/compare-arrays , you can use this code :
const a = [1, 2, 3];
const b = [4, 5, 6];
const c = [1, 2, 3];
function arrayEquals(a, b) {
return Array.isArray(a) &&
Array.isArray(b) &&
a.length === b.length &&
a.every((val, index) => val === b[index]);
}
arrayEquals(a, b); // false
arrayEquals(a, c); // true
https://snack.expo.dev/lmKFsz5J6 finished example

Related

How to save the QR Data and copy my clipboard?

I just have some question.
First, I'm beginner... Very sorry.
I have been developed the react-native app (iOS) for recognize the QR Code.
I already have been succeed the recognize the QR Code on my App.
But My Plan is..
First, I scan the QR Code and I want to save the QR data.
For the purpose, there are many QR Code in the warehouse above many box and it is included Serial Number.
After scanning, I will continue the scan until I want to stop it. (In conclusion, I scan many times.)
I just thought "Let's save the Serial Number at array.
Second, I save the serial number through the scanning and I want to copy our clipboard.
Also, I want to print in my App.
How can I implement this? I have no idea about that.
Code is here.
import { StatusBar } from 'expo-status-bar';
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function App() {
const [hasPermisson, setHasPermisson] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermisson = () => {
(async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setHasPermisson(status == 'granted')
})()
}
useEffect(() => {
askForCameraPermisson ();
}, [])
const handleBarCodeScanned = ({type, data}) => {
setScanned(true);
setText(text);
state = {
sn : [
{
type : type,
data : data
},
{
type : type,
data : data
}
]
}
console.log(state)
}
if (hasPermisson === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permisson</Text>
<StatusBar style="auto" />
</View>
)
}
if(hasPermisson === false) {
return (
<View style={styles.container}>
<Text>No acess to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermisson()}/>
</View>
)
}
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style = {{ height:400, width:400}} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'scan again?'} onPress={( ) => setScanned(false)} color='tomato'/>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width:300,
overflow:'hidden',
borderRadius:30,
backgroundColor:'tomato'
},
maintext: {
fontSize :16,
margin:20
}
});

React Native pincode lock screen create. useState error

I am trying to create a mobile lock screen with numbers to user for entering the pincode.
when the user press the number buttons values should be entered to the array that I have created.
when the numbers are entered, a style property is changed.
here is the code
import React from "react";
import { Alert, StyleSheet, Text, Touchable, TouchableHighlight, TouchableOpacity,useState, View } from "react-native";
import Loading from './Loading';
const Buttons = () => {
this.state = {
passcode: ['','','','']
}
_presControl = num =>{
let tempCode = this.state.passcode;
for(var i = 0; i<tempCode.length;i++){
if(tempCode[i] == ''){
tempCode[i] = num;
break;
}else{
continue;
}
}
this.setState({passcode:tempCode});
};
let nopad = [
{id:1},
{id:2},
{id:3},
{id:4},
{id:5},
{id:6},
{id:7},
{id:8},
{id:9},
{id:0}
];
return(
<View >
<View style={styles.boxcontaner} >
{this.state.passcode.map(p =>{
let style= p !=''? styles.box2:styles.box1;
return <View style={style}></View>
})}
</View>
<View style={styles.noBox}>
{nopad.map(num =>{
return(
<TouchableOpacity style={styles.box}
key={num.id}
onPress={this._presControl(num.id)} >
<Text style={styles.title}>{num.id}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
box1: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray'
},
box2: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray',
backgroundColor:'red'
},
box: {
width:70,
height:70,
borderRadius:70,
borderWidth:1,
borderColor:'#F2F3F4',
alignItems:'center',
backgroundColor:'#F2F3F4',
justifyContent:'center',
alignItems:'center'
},
boxcontaner:{
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between',
marginLeft:40,
marginRight:40,
marginTop:10,
},
noBox:{
alignItems:'center',
justifyContent:'center',
marginTop:100,
flexDirection:'row',
flexWrap:'wrap',
marginLeft:20,
width:270,
height:200,
}
});
export default Buttons;
But when I run the code it says
_this._presControl is not a function. (In '_this._presControl(num.id)', '_this._presControl' is undefined)
what is the error. How can I solve this please ?
You need to create a copy of your array so that you can update the state when a user enters the pin.
_presControl = num =>{
let tempCode = this.state.passcode;
for(var i = 0; i<tempCode.length;i++){
if(tempCode[i] == ''){
tempCode[i] = num;
break;
}else{
continue;
}
}
var newPinCode = [...tempCode];
this.setState(newPinCode);
};
You can not use class methods inside of body of the functional component. Instead you should call you function like that:
<TouchableOpacity
style={styles.box}
key={num.id}
onPress={()=>_presControl(num.id)} >
<Text style={styles.title}>{num.id}</Text>
</TouchableOpacity>

setState not toggle value in react native

i am having function that toggle the state variables value.
the initial value of the state variable is false
Here is my function...
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state.label){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
In the above expression at inital state the value is changed to false then it is not changing to true.
I have also tried.. the below method...
expandLists(label){
this.setState( preState => ({label: !this.preState.label}),console.log(this.state))
}
If you pass the label parameter as a string, then try this:
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state[label]){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
So the difference is in checking if the current value is truethy. In stead of using this.state.label, use this.state[label].
Check this way as you said "label" param type of string
if(this.state.label == "true"){
...
}
or
if(this.state[label]){
...
}
Easy way to achieve this is
toggleLabelValue = (label) => {
this.setState({ [label]: !this.state[label] }, () =>
console.log(this.state)
);
};
Try toggling state in this way:
import React from 'react';
import {
View,
Button,
} from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
label: false
}
}
changeLabel = (currentLabel) => {
this.setState({
label: currentLabel
});
};
toggleLabel = () => {
this.changeLabel(!this.state.label);
};
render() {
return (
<View>
<Button onPress={this.toggleLabel} title="Toggle Label" />
</View>
);
}
}
Here is another implementation using hooks:
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
const [label, setLabel] = useState(false);
const toggleLable = () => {
let temp = label;
setLabel(!temp);
};
return (
<View style={styles.container}>
<TouchableOpacity
onPress={toggleLable}
style={[
styles.btn,
{ backgroundColor: label ? '#4f4' : '#f40' },
]}>
<Text style={styles.text}>{label? "TRUE": "FALSE"}</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
btn: {
width: 200,
height: 200,
borderRadius: 20,
justifyContent: "center"
},
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
alignItems: 'center',
},
text:{
fontSize: 40,
fontWeight: "bold",
color: "white",
textAlign: "center"
}
});
Screenshot:
You can play around with the code here: Toggle Button Example
this works for me using useState:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import SeparateModal from 'components/SeparateModal';
export default function Parent() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View>
<SeparateModal
modalVisible={modalVisible}
setModalVisible = {setModalVisible}
/>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(true) }>Open Modal</Text>
</TouchableOpacity>
</View>
)
}
components/SeparateModal:
export default function SeparateModal({ modalVisible, setmodalVisible }) {
return (
<Modal
visible={ modalVisible }
animationType="slide"
>
<View>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(false) }>Close Modal</Text>
</TouchableOpacity>
</View>
</Modal>
);

React-native useState updating even thought it has not been called

So I'm doing something fairly simple in with react-native. I have a function that creates a copy of the state which is initially an array, I then update the copy and then I would eventually call my setState in order to update the original array. However, when I mutate the copy, for some odd reason it also mutates the array from the state even if I don't call setState. I tried everything that I thought might fix it, using slice, [...copy], splicing it, nothing, it still mutates, and why, I just don't understand why it mutates? If anyone can help me that would be really appreciated.
Here is the function
function completeTaskHandler(goalName, taskId, taskIndex, updateAction) {
const taskSnapShot = tasks.slice();
console.log(taskSnapShot);//logs the copy
console.log(tasks);// logs the original array
taskSnapShot[taskIndex].isComplete = "true";
console.log(taskSnapShot);//results in mutated array
console.log(tasks);//Also results in mutated array why??
};
Here is the full Code block
import React, { useState } from "react";
import { View, StyleSheet, TouchableOpacity } from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { useSelector, useDispatch } from "react-redux";
import { Ionicons } from "#expo/vector-icons";
//Custom Components
import Task from "../../components/local/goals/EditGoalTask";
//Header Custom Component
import CustomBackButton from "../../components/HeaderButtonDark";
//Controllers
import { DefaultText, SmallText, HeaderText, SmallTextItalic } from "../../controllers/TextController";
//Constants
import Colors from "../../constants/Colors";
//Redux reducers
import { udpateTask } from "../../store/actions/user";
const EditGoal = ({ navigation, route }) => {
//Initialize variables
const dispatch = useDispatch();
const { goalId, goalNameFromAddPage } = route.params;
let selectedGoal = useSelector(state => state.userReducer.goals.find((goal) => goal.id === goalId));
if (goalNameFromAddPage) {
selectedGoal = useSelector(state => state.userReducer.goals.find((goal) => goal.goalName === goalNameFromAddPage));
}
//Deconstruct needed variables
const { goalName, startDate, status, tasksArrayOfObjects } = selectedGoal;
//Initialize States
const [tasks, setTasks]= useState(tasksArrayOfObjects.fitler((task) => {
if (!task.isComplete) {
return true;
} else {
return false;
}
}));
//Methods
function deleteTaskHandler(goalName, taskId, taskIndex, updateAction) {
const taskSnapShot = [...tasks];
taskSnapShot.splice(taskIndex, 1);
setTasks(taskSnapShot);
//dispatch(udpateTask(goalName, taskId, updateAction));
};
/* Has issues */
function completeTaskHandler(goalName, taskId, taskIndex, updateAction) {
const taskSnapShot = tasks.slice();
console.log(taskSnapShot);
console.log(tasks);
taskSnapShot[taskIndex].isComplete = "true";
console.log(taskSnapShot);
console.log(tasks);
/*
function copyArray(arr) {
const arrCopy = arr.slice();
const copy = arrCopy.splice(0, arrCopy.length);
return copy;
}
console.log(tasks);
const taskSnapShot = copyArray(tasks);
taskSnapShot[taskIndex].isComplete = true;
console.log(taskSnapShot);
console.log(tasks);
*/
//dispatch(udpateTask(goalName, taskId, updateAction));
};
navigation.setOptions({
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={CustomBackButton}>
<Item
title="BACK"
iconName="md-close"
onPress={() => navigation.popToTop()}
/>
</HeaderButtons>
),
});
return(
<View style={styles.screen}>
<View style={styles.header}>
<View style={styles.goalContainer}>
<SmallText>Goal:</SmallText>
<HeaderText>{goalName}</HeaderText>
</View>
<View style={styles.goalStatusContainer}>
<SmallText style={styles.headerTextMargin}>Started: {startDate}</SmallText>
<View style={{ flexDirection: "row" }}>
<SmallText>Finished: </SmallText>
<SmallTextItalic>{status}</SmallTextItalic>
</View>
</View>
</View>
<View style={styles.pageDescription}>
<DefaultText>
Here is where you can add, delete, or track the steps you need to do in
order to achieve your goal.
</DefaultText>
</View>
<View style={styles.taskContainer}>
{tasks.map((task, index) => {
return <Task
title={task.taskName}
key={"key"+index}
deleteTask={deleteTaskHandler.bind(this, goalName, task.id, index, "delete")}
completeTask={completeTaskHandler.bind(this, goalName, task.id, index, "complete")}
/>
})}
<View style={styles.touchableContainer}>
<TouchableOpacity onPress={() => alert()}>
<View style={{...styles.task, ...styles.addAStepContainer}}>
<View style={styles.addAStep}>
<Ionicons style={{ marginRight: 5 }} name="ios-add" size={23} color={Colors.grey} />
<DefaultText style={{ color: Colors.grey, fontSize: 18 }}>Add a step</DefaultText>
</View>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
screen: {
paddingHorizontal: 10,
},
header: {
flexDirection: "row",
height: 80,
//alignItems: 'center',
},
pageDescription: {
paddingVertical: 10,
},
goalContainer: {
flex: 1,
},
goalStatusContainer: {
flex: 1,
alignItems: "flex-end",
},
headerTextMargin: {
marginBottom: 4,
},
touchableContainer: {
borderRadius: 10,
overflow: "hidden",
},
taskContainer: {
//borderWidth: 1,
},
addAStepContainer: {
paddingVertical: 20,
},
addAStep: {
flexDirection: "row",
},
});
export default EditGoal;
This is a little bit tricky. When you do:
const taskSnapShot = tasks.slice();
you create a new array, but inside that array, you won't duplicate the objects from tasks but just make a reference to these objects. So basically, taskSnapShot is an array of references. So when you modify an object inside taskSnapShot, you also modify it in tasks.
To solve this problem you have to duplicate the object that you want to modify:
const taskSnapShot = [...tasks];
taskSnapShot[taskIndex] = {...taskSnapShot[taskIndex], isComplete: true};

How do I make a list (FlatList) automatically scroll through the elements using Animated?

I have a horizontal FlatList, where each time it reaches the end, it automatically adds new elements to the list, so it kind of is an infinite list. I want the app to scroll through the list by itself automatically, while the user must still be able to scroll back and forth. This is what I have to far
export default class ImageCarousel extends Component {
constructor(props) {
super(props);
this.scrollX = 0;
this.offset = new Animated.Value(0);
this.scrollTo = this.scrollTo.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.stopAnimation = this.stopAnimation.bind(this);
// Listener to call the scrollToOffset function
this.offset.addListener(this.scrollTo);
}
_scroller() {
toValue = this.scrollX + 10; // Scroll 10 pixels in each loop
this.animation = Animated.timing(
this.offset,
{
toValue: toValue,
duration: 1000, // A loop takes a second
easing: Easing.linear,
}
);
this.animation.start(() => this._scroller()); //Repeats itself when done
}
scrollTo(e) {
this.carousel.scrollToOffset({offset: e.value});
}
handleScroll(event) {
// Save the x (horizontal) value each time a scroll occurs
this.scrollX = event.nativeEvent.contentOffset.x;
}
componentDidMount() {
this._scroller();
}
render() {
return (
<View>
<FlatList
ref={el => this.carousel = el}
data={someData}
renderItem={renderFunction}
horizontal={true}
keyExtractor={someKeyFunction}
onEndReached={loadMoreElementsFunction}
onScroll={this.handleScroll}
/>
</View>
);
}
}
It works in the sense that it is automatically scrolling through the list, the problem however, is I cannot manually scroll through the list, since the scroll position is constantly updated by the scrollTo listener. I have tried to add an onPress callback to disable the animation when the FlatList is pressed, I have however not been able to get it to work.
This is my Data.
Blockquote
state = {
link: [
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
],};
Define FlatList Ref
flatList = createRef();
FlatList component
<FlatList
style={{flex: 1}}
data={this.state.link}
keyExtractor={this._keyExtractor.bind(this)}
renderItem={this._renderItem.bind(this)}
horizontal={true}
flatListRef={React.createRef()}
ref={this.flatList}
/>
Next slide
_goToNextPage = () => {
if (CurrentSlide >= this.state.link.length-1) CurrentSlide = 0;
this.flatList.current.scrollToIndex({
index: ++CurrentSlide,
animated: true,
});
};
Start and stop Interval
_startAutoPlay = () => {
this._timerId = setInterval(this._goToNextPage, IntervalTime);
};
_stopAutoPlay = () => {
if (this._timerId) {
clearInterval(this._timerId);
this._timerId = null;
}
};
Associated function
componentDidMount() {
this._stopAutoPlay();
this._startAutoPlay();
}
componentWillUnmount() {
this._stopAutoPlay();
}
_renderItem({item, index}) {
return <Image source={{uri: item}} style={styles.sliderItems} />;
}
_keyExtractor(item, index) {
return index.toString();
}
Full Code:
import React, {Component, createRef} from 'react';
import {
Text,
View,
ScrollView,
Image,
StyleSheet,
Dimensions,
FlatList,
} from 'react-native';
let CurrentSlide = 0;
let IntervalTime = 4000;
export default class Slider extends Component {
flatList = createRef();
// TODO _goToNextPage()
_goToNextPage = () => {
if (CurrentSlide >= this.state.link.length-1) CurrentSlide = 0;
this.flatList.current.scrollToIndex({
index: ++CurrentSlide,
animated: true,
});
};
_startAutoPlay = () => {
this._timerId = setInterval(this._goToNextPage, IntervalTime);
};
_stopAutoPlay = () => {
if (this._timerId) {
clearInterval(this._timerId);
this._timerId = null;
}
};
componentDidMount() {
this._stopAutoPlay();
this._startAutoPlay();
}
componentWillUnmount() {
this._stopAutoPlay();
}
// TODO _renderItem()
_renderItem({item, index}) {
return <Image source={{uri: item}} style={styles.sliderItems} />;
}
// TODO _keyExtractor()
_keyExtractor(item, index) {
// console.log(item);
return index.toString();
}
state = {
link: [
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
// 'https://picsum.photos/200/300',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
'https://image.shutterstock.com/image-vector/online-exam-computer-web-app-260nw-1105800884.jpg',
],
};
render() {
return (
<View style={{marginTop: 10, marginBottom: 10}}>
<FlatList
style={{
flex: 1,
// TODO Remove extera global padding
// marginLeft: -size.padding,
// marginRight: -size.padding,
}}
data={this.state.link}
keyExtractor={this._keyExtractor.bind(this)}
renderItem={this._renderItem.bind(this)}
horizontal={true}
flatListRef={React.createRef()}
ref={this.flatList}
/>
</View>
);
}
}
const styles = StyleSheet.create({
sliderItems: {
marginLeft: 5,
marginRight: 5,
height: 200,
width: Dimensions.get('window').width,
},
});
Just in case you're still not found the answer yet,
this is my approach to create autoscroll carousel using FlatList
import React, { Component } from 'react'
import {
StyleSheet,
View,
FlatList,
ScrollView,
Dimensions,
Image
} from 'react-native'
const { width } = Dimensions.get('window');
const height = width * 0.2844;
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
sliderIndex: 0,
maxSlider: 2,
banners: [
{_id: 1, imageUrl: 'https://www.do-cart.com/img/slider/1.jpg'},
{_id: 2, imageUrl: 'https://www.do-cart.com/img/slider/2.jpg'},
{_id: 3, imageUrl: 'https://www.do-cart.com/img/slider/3.jpg'},
],
}
}
setRef = (c) => {
this.listRef = c;
}
scrollToIndex = (index, animated) => {
this.listRef && this.listRef.scrollToIndex({ index, animated })
}
componentWillMount() {
setInterval(function() {
const { sliderIndex, maxSlider } = this.state
let nextIndex = 0
if (sliderIndex < maxSlider) {
nextIndex = sliderIndex + 1
}
this.scrollToIndex(nextIndex, true)
this.setState({sliderIndex: nextIndex})
}.bind(this), 3000)
}
render() {
return (
<View style={styles.container}>
<View style={{height: 80, backgroundColor: '#123866', width:'100%'}}></View>
<ScrollView style={styles.scrollContainer} showsVerticalScrollIndicator={false}>
<FlatList
ref={this.setRef}
data={this.state.banners}
horizontal
showsHorizontalScrollIndicator={false}
pagingEnabled
keyExtractor={item => item._id}
renderItem={({item, i}) => (
<View key={i} style={{ height, width}}>
<Image style={{ height, width }} source={{ uri: item.imageUrl }} />
</View>
)}
onMomentumScrollEnd={(event) => {
let sliderIndex = event.nativeEvent.contentOffset.x ? event.nativeEvent.contentOffset.x/width : 0
this.setState({sliderIndex})
}}
/>
<View style={styles.sliderContainer}>
{
this.state.banners.map(function(item, index) {
return (
<View key={index} style={styles.sliderBtnContainer}>
<View style={styles.sliderBtn}>
{
this.state.sliderIndex == index ? <View style={styles.sliderBtnSelected}/> : null
}
</View>
</View>
)
}.bind(this))
}
</View>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
scrollContainer: {
flex: 1
},
sliderContainer: {
flexDirection: 'row',
position: 'absolute',
top: 80,
alignSelf: 'center'
},
sliderBtn: {
height: 13,
width: 13,
borderRadius: 12,
borderWidth: 1,
borderColor: 'white',
alignItems: 'center',
justifyContent: 'center',
marginRight: 10
},
sliderBtnSelected: {
height: 12,
width: 12,
borderRadius: 6,
backgroundColor: 'white',
},
sliderBtnContainer: {
flexDirection: 'row', marginBottom: 24
},
});
https://snack.expo.io/rJ9DOn0Ef
For those looking for a function-based component, this is my approach. The user can interact with the carousel and the automatic scroller will simply continue from the current slide.
The trick to achieving this is using an "onViewableItemsChanged" callback, where the "itemVisiblePercentThreshold" is >= 50%. This ensures the callback fires after the scroll to the new page is more than 50% complete (otherwise the automatic scroller triggers the callback to early and makes it scroll back).
import { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions } from "react-native";
import { FlatList, Image, StyleSheet } from "react-native";
const width = Dimensions.get("screen").width;
export const CarouselAutoScroll = ({ data, interval }) => {
const imageRef = useRef();
const [active, setActive] = useState(0);
const indexRef = useRef(active);
indexRef.current = active;
useInterval(() => {
if (active < Number(data?.length) - 1) {
setActive(active + 1);
} else {
setActive(0);
}
}, interval);
useEffect(() => {
imageRef.current.scrollToIndex({ index: active, animated: true });
}, [active]);
const onViewableItemsChangedHandler = useCallback(
({ viewableItems, changed }) => {
if (active != 0) {
setActive(viewableItems[0].index);
}
},
[]
);
return (
<FlatList
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={onViewableItemsChangedHandler}
viewabilityConfig={{
itemVisiblePercentThreshold: 50,
}}
ref={imageRef}
pagingEnabled
data={data}
horizontal
renderItem={({ item, index }) => (
<Image
key={index}
source={item.image}
resizeMode={"contain"}
style={{
flex: 1,
height: "100%",
width: width,
}}
/>
)}
style={{ ...StyleSheet.AbsoluteFill }}
/>
);
};
const useInterval = (callback, delay) => {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};

Resources