How do I increment i in useState? - react-native

I have been struggling for hours and couldn't figuring out how to increment i using useState([]). I know useState([]) is an asynchronous function, and I don't know how to increment i properly to give unique keys to each Text
Here is my code -
import React, { Component, useState, useEffect } from "react";
import {
View,
Text,
StyleSheet,
TextInput
} from 'react-native';
let weatherPanel = []
function WeatherApp(){
const [data, setData] = useState([]);
let i = 0;
const dates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const temperatures = [20, 21, 26, 19, 30, 32, 23, 22, 24];
const cities = ['LA', 'SAN', 'SFO', 'LGA', 'HND', 'KIX', 'DEN', 'MUC', 'BOM'];
const buttonPressed = () => {
weatherPanel.push(
<View style = {styles.weatherBoard}>
<Text key = {dates[i]} style = {styles.date}>{dates[i]}</Text>
<Text key = {temperatures[i]} style = {styles.temperature}>{temperatures[i]}</Text>
<Text key = {cities[i]} style = {styles.cityName}>{cities[i]}</Text>
</View>
)
setData(weatherPanel);
i = i + 1;
}
// learn this to increment i properly
useEffect(()=>{
console.log(i)
}, [])
return(
<View style={styles.appBackground}>
<View style = {styles.searchBar}>
<TextInput>Text</TextInput>
</View>
<View style = {styles.weatherPanel}>
{data}
</View>
<View style = {styles.addButton}>
<Text onPress={() => buttonPressed(i)} style = {styles.temperature}>Text</Text> // CALLED buttonPressed() HERE.
</View>
</View>
)
}
I am calling button buttonPressed at the comment, //CALLED buttonPressed() HERE.. I am trying to add i, that is mentioned above, to access elements of all equal length lists. Could someone help me?
Thanks!

The effect runs once on load and then when any of the values in the dependency array change. Your useEffect has an empty dependency array, so it will never fire after load. If you want to run the effect when i changes, it should be
useEffect(()=>{
console.log(i)
}, [i]);
Read more about useEffect here in the docs.
If you have control over the data you're using, it's more intuitive to store all the related info in one object, rather than in three separate arrays linked only by index.
const weatherArray = [
{ date: 1, temperature: 20, city: 'LA' },
// ...
You should also store i in state rather than as a plain variable. Keeping it in state will tell React to re-render the component when it changes.
const [i, setI] = useState(0);
// ...
setI(oldI => oldI + 1);

In your case, you would like to display data in loops for some data set. I would suggest you to solve it without iterator and use map() function instead.
import React, { Component, useState, useEffect } from "react";
import {
View,
Text,
StyleSheet,
TextInput
} from 'react-native';
function WeatherApp(){
const [data, setData] = useState([]);
const dates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const temperatures = [20, 21, 26, 19, 30, 32, 23, 22, 24];
const cities = ['LA', 'SAN', 'SFO', 'LGA', 'HND', 'KIX', 'DEN', 'MUC', 'BOM'];
const buttonPressed = () => {
if (data.length >= dates.length) return; //Prevent out of bound
setData([
...data, //get old data array
{
//New data set
dates: dates[data.length],
temperatures: temperatures[data.length],
cities: cities[data.length]
}
]);
}
return (
<View style={styles.appBackground}>
<View style={styles.searchBar}>
<TextInput>Text</TextInput>
</View>
<View style={styles.weatherPanel}>
{
//Loop the data by map function
data.map((item, index) => {
return (
<View style={styles.weatherBoard}>
<Text key={item.dates} style={styles.date}>{item.dates}</Text>
<Text key={item.temperatures} style={styles.temperature}>{item.temperatures}</Text>
<Text key={item.cities} style={styles.cityName}>{item.cities}</Text>
</View>
)
})
}
</View>
<View style={styles.addButton}>
<Text onPress={() => buttonPressed()} style={styles.temperature}>Add Item</Text>
</View>
</View>
)
}

Related

React native - how to detect space press on keyboard?

I am building a chat app and trying to count how many times the user pressed on 'space' key every time they press it and not counting it on the final outcome of their textinput entery.
<TextInput onKeyPress={(e) => {
let pressedKey = e.nativeEvent.key;
if(pressedKey === ' '){
pressedKey = 'Space';
} />
This code example works for me only if the user press two times on space
Here is the code that do the count for you:
import React, {useState} from 'react';
import {StyleSheet, Text, View, TextInput} from 'react-native';
const App = () => {
const [spaceCount, setSpaceCount] = useState(0);
const onInputKeyPressHandler = e => {
e.nativeEvent.key === ' ' &&
setSpaceCount(oldSpaceCount => oldSpaceCount + 1);
};
return (
<View style={styles.container}>
<Text>Hello friend :)</Text>
<TextInput style={styles.input} onKeyPress={onInputKeyPressHandler} />
<Text>{`Space Count: ${spaceCount}`}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
paddingTop: 100,
paddingHorizontal: 30,
},
input: {
height: 50,
borderWidth: 1,
marginVertical: 10,
paddingHorizontal: 20,
},
});
export default App;
try using a counter useState. when the user presses on space, the counter increments by 1
const [spaceCounter, setSpaceCounter] = useState(0)
<TextInput onKeyPress={(e) => {let pressedKey = e.nativeEvent.key;
if(pressedKey === ' ')
{
pressedKey = 'Space';
setSpaceCounter = spaceCounter +1;`
}/>

React-Native: Ref is missing scrollToIndex() for FlatList

I am passing a ref to a FlatList and expecting to access the scrollToIndex() function, but it doesn't appear in the console and it throws an error when I try to use it saying "scrollTo() is not a function", it doesn't even recognise that I'm trying to use scrollToIndex().
This is a simple example of usage in my code:
const ref = useRef()
ref.current?.scrollToIndex({index:itemIndex,animated:true})
<FlatList ref={ref} .../>
Has anyone experienced this and knows how to make it work? I've looked over the documentation and examples and everything seems to be fine. Any help would be appreciated!
UPDATE:
Component:
import React from "react";
import { View, FlatList, ImageBackground,TouchableOpacity,ScrollView,StyleSheet, Text} from "react-native";
const Projects = ({ index, navigation }) => {
const {width} = Dimensions.get("window")
const [imageIndex, setIndex] = React.useState(0)
const ref = React.useRef()
const wItem = (width - 63) / 2;
console.log("index projects", imageIndex)
const scrollToIndex = (index)=>{
if(ref.current){
ref.current.scrollToIndex({index:index})
}
}
const goToIndex = React.useCallback((info) => {
const wait = new Promise(resolve => setTimeout(resolve, 200));
wait.then(() => {
ref.current?.scrollToIndex({ index: info.index, animated: true });
})
}, [])
return (
<ScrollView >
<TouchableOpacity onPress={()=> scrollToIndex(5)}><Text>Scroll to</Text></TouchableOpacity>
<FlatList ref={ref}
nestedScrollEnabled
numColumns={2}
data={DATA}
onScrollToIndexFailed={(index)=>goToIndex(index)}
style={styles.flatList}
keyExtractor={(i, index) => index.toString()}
renderItem={({ item, index }) => (
<View style={styles.item}>
<ImageBackground
source={item.image}
/* #ts-ignore */
imageStyle={styles.imgStyle}
style={{ width: wItem, height: wItem, alignItems: "flex-end" }}>
</ImageBackground>
</TouchableOpacity>
<Text>{item.title}</Text>
</View>
)}
/>
</ScrollView>
);
};
export default Projects;
const styles = StyleSheet.create({
container: {
flex: 1,
},
item: {
marginRight: 15,
marginBottom: 24,
alignItems:'center'
},
flatList: {
marginHorizontal: 24,
marginTop: 16,
},
imgStyle: {
borderRadius: 12,
},
});
const DATA = [//list of local images]
It is unable to call scrollTo because the ref has only been initialized and not assigned at the time of use:
const ref = useRef()
// ref = null
ref.current?.scrollToIndex({index:itemIndex,animated:true})
// ref = null
<FlatList ref={ref} .../>
// ref != null
So ref is not assigned until you render for the first time. This means that if you want to call .scrollTo() on the ref you can do it in 2 different ways:
1 - If you want to scroll to a certain index initially:
useEffect(() => {
const initialIndex = 34 //random number
if (ref.current){
ref.current.scrollToIndex({index: initialIndex})
}
}, [ref])
The ref in the dependency array will make sure that the useEffect will run whenever the value of ref changes (in this case it will change once it is assigned to a value by rendering the <Flatlist />
2 - If you want to scroll to a certain index on demand:
const handleScrollTo = (index) => {
if (ref.current && typeof ref.current.scrollToIndex === 'function'){
ref.current.scrollToIndex({index: index})
}
}
This function should be connected to something like an onPress or similar, which means the ref should be defined whenever the user presses the button because being able to see the button and press it requires that the component has rendered. Even though ref.current should already be assigned once it is used here, it is always good practice to make sure that it doesn't crash in cases where it is not, which is why we are adding the if-statement.

Embed a time picker in a bottomsheet

In my React Native app, I want to embed a time picker inside of a bottom sheet. Currently the time picker displays when tapped using onPress={showTimepicker} .
TimePicker.js
import React, { useState, useRef } from "react";
import RBSheet from "react-native-raw-bottom-sheet";
import { Text, View, TouchableOpacity, Platform } from "react-native";
import NotificationOff from "../../../components/ImageComponents/NotificationIcons/NotificationOff";
import DateTimePicker from "#react-native-community/datetimepicker";
import Animated from "react-native-reanimated";
const DailyNotification = () => {
const refRBSheet = useRef();
let fall = new Animated.Value(1);
const animatedShadowOpacity = Animated.interpolateNode(fall, {
inputRange: [0, 1],
outputRange: [0.5, 0],
});
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled((previousState) => !previousState);
const [date, setDate] = useState(new Date(1598051730000));
const [mode, setMode] = useState("date");
const [show, setShow] = useState(false);
const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;
setShow(Platform.OS === "ios");
setDate(currentDate);
};
const showMode = (currentMode) => {
setShow(true);
setMode(currentMode);
};
return (
<View style={styles.userAreaContainer}>
<TouchableOpacity onPress={() => refRBSheet.current.open()}>
<NotificationOff />
</TouchableOpacity>
<RBSheet
ref={refRBSheet}
animationType="fade"
height={500}
closeOnDragDown={true}
closeOnPressMask={false}
customStyles={{
wrapper: {
backgroundColor: "transparent",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.8,
shadowRadius: 3.84,
elevation: 5,
},
draggableIcon: {
backgroundColor: "#000",
},
}}
>
<View style={styles.notifButtonWrapper}>
<View style={{ width: "100%" }}>
<TouchableOpacity onPress={showTimepicker}>
<Text> Set Notification Time </Text>
</TouchableOpacity>
{show && (
<DateTimePicker
testID="dateTimePicker"
value={date}
mode="time"
is24Hour={true}
display="inline"
onChange={onChange}
/>
)}
</View>
<TouchableOpacity style={styles.saveButton}>
<Text style={styles.link}>Save</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
</RBSheet>
</View>
);
};
export default DailyNotification;
This old S/O post describes how it is done in Android, but I haven't been able to find a React Native example.
I've seen this done in other apps (example below) but have not been able to figure out how to do this. Any ideas or libraries that already have this capability built in? Thanks in advance.
You can use react-native-date-picker package. It has two different modes, modal and inlined. You can put the inlined version inside a view. As far as I understand, you don't want to see timepicker as a modal, you want to see it as inlined/embedded. Check the inlined usage of the package. This should solve your problem.

React Native Datatable doesn't show after moving the fragment into Component

I've created the following component. React Native Paper Datatable rows aren't showing after moving it into component and linking it to json loop.
If we comment " and uncommented the commented block below, you will see the Datatable is showing. What am I doing wrong with my two components? I've done all console.log. All data are showing correctly but JSX elements aren't rendering inside Datatable.
I've created the following code on Snack: https://snack.expo.dev/#everestster/datatable-component
import React, {useEffect} from 'react';
import type {Node} from 'react';
import {View, ScrollView, Text, StyleSheet, Dimensions} from 'react-native';
import {DataTable as PaperDataTable} from 'react-native-paper';
const DataTable = props => {
const optionsPerPage = [2, 3, 4];
const [page, setPage] = React.useState(0);
const [itemsPerPage, setItemsPerPage] = React.useState(optionsPerPage[0]);
useEffect(() => {
setPage(0);
}, [itemsPerPage]);
const HeaderSection = (): Node => {
console.log(props.items);
if (props.items.length === 0) {
return;
}
return (
<PaperDataTable.Header>
{Object.keys(props.items[0]).forEach(function (key) {
if (key !== 'Id') {
<PaperDataTable.Title style={[styles.allCell]}>
{key}
</PaperDataTable.Title>;
}
})}
</PaperDataTable.Header>
);
};
const BodySection = (): Node => {
return (
<PaperDataTable.Row>
{Object.keys(props.items[0]).forEach(function (key) {
if (key !== 'Id') {
<PaperDataTable.Cell style={[styles.allCell]}>
{key}
</PaperDataTable.Cell>;
}
})}
</PaperDataTable.Row>
);
};
return (
<ScrollView style={styles.tableHolder}>
<ScrollView horizontal={true}>
<View style={{alignItems: 'center'}}>
<PaperDataTable style={styles.table}>
<HeaderSection />
<BodySection />
{/*<PaperDataTable.Header>
<PaperDataTable.Title>Name</PaperDataTable.Title>
<PaperDataTable.Title>Email</PaperDataTable.Title>
</PaperDataTable.Header>
<PaperDataTable.Row>
<PaperDataTable.Cell>John</PaperDataTable.Cell>
<PaperDataTable.Cell>john#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>
<PaperDataTable.Row>
<PaperDataTable.Cell>Harry</PaperDataTable.Cell>
<PaperDataTable.Cell>harr#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>
<PaperDataTable.Row>
<PaperDataTable.Cell>Jessica</PaperDataTable.Cell>
<PaperDataTable.Cell>jessica#gmail.com</PaperDataTable.Cell>
</PaperDataTable.Row>*/}
<PaperDataTable.Pagination
page={page}
numberOfPages={1}
onPageChange={p => setPage(p)}
optionsPerPage={optionsPerPage}
itemsPerPage={itemsPerPage}
setItemsPerPage={setItemsPerPage}
showFastPagination
optionsLabel={'Rows per page'}
/>
</PaperDataTable>
</View>
</ScrollView>
</ScrollView>
);
};
const styles = StyleSheet.create({
tableHolder: {},
table: {
paddingLeft: 50,
paddingRight: 50,
flex: 1,
},
allCell: {
marginRight: 20,
},
});
export {DataTable};
Any help will be appreciated.
The problem is in your structure. Your current BodySection is not returning the correct structure react-native-paper wants. I rewrote the BodySection function. Here is the snack: https://snack.expo.dev/#truetiem/datatable-component
const BodySection = (): Node => {
return props.items.map(function (item) {
return (
<PaperDataTable.Row>
{Object.keys(item).map((key) => key === 'Id' ? null : (
<PaperDataTable.Cell>
{item[key]}
</PaperDataTable.Cell>
))}
</PaperDataTable.Row>
);
});
};

How to change state value in const mode?

I am a beginner in React-Native.
I tried to change the state value.
But it seems different with "class" mode. Now I am using "const" mode.
In const mode, how can I change the state value?
import React from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
this.state = {
checkIconURL : require('../../assets/ic_check_circle.png')
}
set = () => {
this.setState({ checkIconURL : require('../../assets/empty.png') }) //But this lines gives err.
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width: 30, height: 30}} />
</TouchableOpacity>
</View>
)
}
state is not available in function components, this.state and this.setState() can only be used in class component, u have to use useState hook from react to use the state...
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [iconUrl, setIconUrl ] = useState(initialState);
const set = () => {
const emptyImageUrl = require('../../assets/empty.png')
setIconUrl(emptyImageUrl);
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {set}>
<Image source = {iconUrl} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}
the following code is incorrect as you need to use Hook in a functional component to manage state.
React provides the useState hook to manage state in a functional component.
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [ state,setState ] = useState({checkIconURL: initialState});
set = () => {
const emptyImageUrl = require('../../assets/empty.png') });
setState({...state, checkIconURL: emptyImageUrl })
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}