React native <RNDatePicker> not closing on first try - react-native

so i have a component that uses textinput and rndatepicker
it works like this:
1) when textinput is focused datepicker must appear.
2) after selecting date the value of textinput changes to selected date.
my problem is im having to close the modal twice and the other components value changes with the operation on current component.
here is the code
import * as React from 'react';
import {Modal, Platform} from 'react-native';
import {TextInput} from 'react-native-paper';
import RNDatePicker, {Event} from '#react-native-community/datetimepicker';
import moment from 'moment';
interface IProps {
value: string;
onChange: (value: string, date: Date | undefined) => void;
}
const isIOS = Platform.OS === 'ios';
let visible = false;
const DatePicker = ({value, onChange}: IProps) => {
const [isVisible, setVisible] = React.useState(false);
const renderDatePicker = () => {
const datepicker = (
<RNDatePicker
value={value ? moment(value).toDate() : new Date()}
minimumDate={value ? moment(value).toDate() : new Date()}
onChange={(_: Event, date: Date | undefined) => {
onChange(moment(date).format('YYYY/MM/DD'), date);
visible = !visible;
}}
display="calendar"
/>
);
return isIOS ? (
<Modal
transparent
visible={isVisible}
supportedOrientations={['portrait']}
onRequestClose={() => {
setVisible(false);
}}>
{datepicker}
</Modal>
) : (
datepicker
);
};
return (
<>
<TextInput
value={value ? moment(value).format('YYYY/MM/DD') : ''}
onFocus={() => {`
setVisible(!visible);
visible = true;
}}
onBlur={() => {
setVisible(false);
visible = false;
}}
keyboardAppearance="dark"
style={{backgroundColor: 'transparent'}}
/>
{/* { if(visible) renderDatePicker()} */}
{visible && renderDatePicker()}
</>
);
};
export default DatePicker;

You have to use onChange/onFocus function as per below ...
const onChange = (event, selectedDate) => {
if (selectedDate === undefined) {
setVisible(false);
} else {
setDate(currentDate);
}
};
ref. https://github.com/react-native-community/datetimepicker

Related

Render after fetching async data

I am fetching data in useEffect() and then modifying the object (modifying clients.unreadMessages based on what should render icon), which is later sent to the component to render. But this component does not render correctly always, the icon is sometimes missing. I think it's because data are modified after rendering.
ClientList
import Colors from '#helper/Colors';
import { useSelector } from '#redux/index';
import { HealthierLifeOption } from '#redux/types';
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import ClientItem from './ClientItem';
import {usePubNub} from "pubnub-react";
export type Client = {
id: number;
username: string;
individualPlanPaid: boolean;
healthierLifeOption?: HealthierLifeOption;
company?: {
id: number;
companyName: string;
};
mentor?: {
id: number;
username: string;
};
}
type Props = {
navigation: any;
clients: Client[];
isAdmin?: boolean;
isLoading: boolean;
onEndReached: Function;
fromAdminTab?: boolean
}
const ClientList = ({ navigation, clients, onEndReached, isLoading, isAdmin = false, fromAdminTab= false }: Props) => {
const resultCount = useSelector(state => state.user.menteesResultCount);
const [hasMoreResults, setHasMoreResults] = useState(true);
const userId = useSelector(state => state.user.id);
const pubnub = usePubNub();
useEffect(() => {
setHasMoreResults(resultCount !== clients?.length);
}, [resultCount, clients]);
useEffect(() => {
getUnreadMessagesProccess().then(r => console.log("aaaaaaaaa"));
}, []);
async function setGrant() {
return new Promise( async resolve => {
await pubnub.grant({
channels: [userId.toString() + '.*'],
ttl: 55,
read: true,
write: true,
update: true,
get: true,
}, response => resolve(response));
});
}
async function getMetadata() {
const options = {include: {customFields: true}};
return await pubnub.objects.getAllChannelMetadata(options);
}
function setChannelsAndTokens(channelMetadata, channels, tokens) {
channelMetadata.data.forEach((value, index) => {
tokens.push(value.custom.lastToken);
channels.push(value.id)
});
}
async function getUnreadedMessages(channels, tokens) {
return await pubnub.messageCounts({
channels: channels,
channelTimetokens: tokens,
});
}
async function getUnreadMessagesProccess() {
const tokens = ['1000'];
const channels = ['1234567'];
const auth = await setGrant();
const channelMetadata = await getMetadata();
const l = await setChannelsAndTokens(channelMetadata, channels, tokens);
const unread = await getUnreadedMessages(channels, tokens).then((res) => {
clients.forEach((value, index) => {
if (res.channels[value.id + '-' + userId + '-chat']) {
value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
} else {
value.unreadMessages = 0;
}
})
console.log(res);
});
return unread;
}
return (
<View>
<FlatList
keyExtractor={item => item.id.toString()}
data={clients}
onEndReached={() => hasMoreResults ? onEndReached() : null}
onEndReachedThreshold={0.4}
renderItem={item => (
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
/>
)}
ListFooterComponent={isLoading
? () => (
<View style={{ padding: 20 }}>
<ActivityIndicator animating size="large" color={Colors.border_gray} />
</View>
)
: null
}
/>
</View>
);
}
export default ClientList;
ClientItem
import React, {useEffect, useState} from 'react';
import { Text, View, Image, TouchableOpacity } from 'react-native';
import Images from '#helper/Images';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import Colors from '#helper/Colors';
import styles from '../styles';
import ClientModal from './ClientModal/ClientModal';
import { Client } from './ClientList';
import { HealthierLifeOption } from '#redux/types';
type Props = {
client: Client;
navigation: any;
isAdmin?: boolean;
fromAdminTab?: boolean
}
const ClientItem = ({ client, navigation, isAdmin = false, fromAdminTab= false }: Props) => {
const [showModal, setShowModal] = useState(false);
const [showIcon, setShowIcon] = useState(false)
console.log(client.unreadMessages)
useEffect(() =>{
if(client.unreadMessages>0)
setShowIcon(true);
},[client.unreadMessages]);
let clientIcon = Images.icon.logoIcon;
const handleContinueButton = () => {
if(!fromAdminTab) {
navigation.navigate('ChatFromClientsList', { selectedId: client.id, showBackButton: true });
}else {
setShowModal(!showModal)
}
};
return (
<View style={styles.client_item_container}>
<TouchableOpacity style={styles.client_content_container} onPress={handleContinueButton}
>
<View style={styles.image_container}>
<Image
style={styles.client_profile_img}
source={clientIcon}
resizeMode="contain"
resizeMethod="resize"
/>
</View>
<View style={styles.title_container}>
<Text style={styles.title} >{ client.username } </Text>
</View>
<View style={styles.dot_container}>
{showIcon && <FontAwesome5
name="comment-dots"
size={20}
color={Colors.red}
/> }
</View>
<View style={styles.hamburger_container} >
<FontAwesome5
name="bars"
size={30}
color={Colors.black}
onPress={() => setShowModal(!showModal)}
/>
</View>
<View>
<ClientModal
isVisible={showModal}
onCollapse={(value: boolean) => setShowModal(value)}
closeModal={(value: boolean) => setShowModal(value)}
client={client}
navigation={navigation}
isAdmin={isAdmin}
/>
</View>
</TouchableOpacity>
</View>
);
};
export default ClientItem;
This code does not render correctly always:
{showIcon && <FontAwesome5
name="comment-dots"
size={20}
color={Colors.red}
/> }
You should not calculate the value in renderItem.
Why you don’t calc the value outside of renderItem in pass it in
useEffect(() =>{
if(client.unreadMessages>0)
setShowIcon(true);
},[client.unreadMessages]);
Do something like:
renderItem={item => (
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
showIcon={item.item.unreadMessages>0}
/>
)}
By the way renderItem should not be a anonymous function.

how can i display the selected time from a timepicker in a textinput react native

I used this timepicker my objective is to select a time from the picker and display it in my text inputs everything is working fine up until I select the time it won't show and gives me an error that text input value has to be a string can anyone tell me what I'm doing wrong here is my code so far
import React, { useState } from 'react';
import { View, TextInput, Text, Platform, Button } from 'react-native';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
const AttendanceMain = () => {
const [showPicker, setShowPicker] = useState(false);
const [selectedTime, setSelectedTime] = useState('');
const showTimePicker = () => {
setShowPicker(true);
};
const hideTimePicker = () => {
setShowPicker(false);
};
const handleConfirm = (time) => {
setSelectedTime(time);
hideTimePicker();
};
return (
<View>
<Text>From:</Text>
<TextInput
placeholder="00:00"
keyboardType="numeric"
onFocus={showTimePicker}
value={selectedTime}
onChange={handleConfirm}
/>
<Text>To:</Text>
<TextInput
placeholder="00:00"
keyboardType="numeric"
onFocus={showTimePicker}
value={selectedTime}
onChange={handleConfirm}
/>
<DateTimePickerModal
isVisible={showPicker}
mode="time"
is24Hour={true}
onConfirm={handleConfirm}
onCancel={hideTimePicker}
/>
</View>
);
};
export default AttendanceMain;
Why don't you use react-native-datetimepicker/datetimepicker
Working Example
and Implement it like this
import React, { useState } from 'react';
import { View, Button, Platform, Text, TextInput } from 'react-native';
import DateTimePicker from '#react-native-community/datetimepicker';
const App = () => {
const [From, setFrom] = useState(new Date());
const [To, setTo] = useState(new Date());
const [mode, setMode] = useState('date');
const [show, setShow] = useState(false);
const [currentSetting, setcurrentSetting] = useState('from');
const onChange = (event, selectedDate) => {
if (currentSetting === 'from') {
const currentDate = selectedDate || From;
setShow(Platform.OS === 'ios');
setFrom(currentDate);
} else {
const currentDate = selectedDate || To;
setShow(Platform.OS === 'ios');
setTo(currentDate);
}
};
const showTimepicker = (current) => {
setShow(true);
setcurrentSetting(current);
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<View>
<Text>From:</Text>
<TextInput
placeholder="00:00"
keyboardType="numeric"
onFocus={() => showTimepicker('from')}
value={From.toLocaleTimeString()}
/>
<Text>To:</Text>
<TextInput
placeholder="00:00"
keyboardType="numeric"
onFocus={() => showTimepicker('to')}
value={To.toLocaleTimeString()}
/>
</View>
{show && (
<DateTimePicker
testID="dateTimePicker"
value={From}
mode={'time'}
is24Hour={true}
display="default"
onChange={onChange}
/>
)}
</View>
);
};
export default App;

Error when simulate change text react native with jest

Let's say I create a login screen. Inside that screen, I import form component. And inside form component I have a text input.
Then, I want to simulate text input on change text, but always get an error
Method “simulate” is meant to be run on 1 node. 0 found instead.
This is my test file
it('calls the login submit method', () => {
const fieldPhoneNumber = wrapper
.find('Form')
.dive()
.find('TextInput[id="fieldPhoneNumber"]');
fieldPhoneNumber
.at(0)
.simulate('changeText', { target: { value: '082262366193' } });
});
This is my component login file
import React, { useState, useEffect } from 'react';
import { ScrollView, StatusBar, Platform } from 'react-native';
import Header from './components/Header';
import Form from './components/Form';
import ButtonSocialMedia from './components/ButtonSocialMedia';
function LoginScreen() {
const [phoneNumber, setPhoneNumber] = useState('');
const [focus, setFocus] = useState(false);
useEffect(() => {
}, [phoneNumber]);
const changePhoneNumber = (value) => {
setPhoneNumber(value);
};
const showAppleButton = () => {
if (Platform.OS === 'ios') {
const version = Platform.Version.split('.')[0];
if (version >= 13) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const loginSubmit = () => {
console.log('Login Submit');
};
return (
<ScrollView>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<Header />
<Form
phoneNumber={phoneNumber}
changePhoneNumber={(value) => changePhoneNumber(value)}
focus={focus}
setFocus={() => setFocus(true)}
loginSubmit={() => loginSubmit()} />
<ButtonSocialMedia showAppleButton={() => showAppleButton()} />
</ScrollView>
);
}
export default LoginScreen;
This is my form component
/* eslint-disable prettier/prettier */
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import styles from '../styles/StyleForm';
import color from '../../../../__global__/styles/themes/colorThemes';
import regex from '../../../../constant/regex';
import * as yup from 'yup';
import { Formik } from 'formik';
import ButtonFull from '../../../../__global__/button/buttonFull';
const regexPhoneNumber = regex.phone;
function Form(props) {
const renderFocus = () => {
if (props.focus) {
return (
<Text style={styles.textFocus}>Type your phone number</Text>
);
}
};
return (
<Formik
enableReinitialize={true}
initialValues={{
phoneNumber: props.phoneNumber,
}}
onSubmit={values => {
console.log('Login Submit');
}}
validateOnMount={true}
validationSchema={yup.object().shape({
phoneNumber: yup
.string()
.required()
.min(8)
.matches(regexPhoneNumber, 'Phone number is not valid'),
})}>
{({
// values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit,
}) => (
<View style={styles.form}>
<View style={styles.subContainer}>
<View style={styles.containerTitle}>
<Text style={styles.textTitle}>+62</Text>
</View>
<View style={styles.containerPhoneNumber}>
{renderFocus()}
<TextInput
id={'fieldPhoneNumber'}
onFocus={() => props.setFocus(true)}
value={props.phoneNumber}
style={styles.subContainerPhoneNumber}
placeholderStyle={styles.placeholder}
placeholder={'Type your phone number'}
onChangeText={(value) => {
handleChange('phoneNumber');
props.changePhoneNumber(value);
setFieldTouched('phoneNumber', true);
}}
keyboardType={'numeric'}
onBlur={() => setFieldTouched('phoneNumber', true)}
/>
</View>
</View>
{touched.phoneNumber && errors.phoneNumber && (
<View style={styles.containerError}>
<Text style={styles.textError}>Phone number is not valid</Text>
</View>
)}
<View style={styles.containerButton}>
<ButtonFull
isDisabled={!isValid}
id={'buttonLogin'}
color={isValid ? color.thema : color.grey}
handleSubmit={() => props.loginSubmit()}
title={'Next'}
/>
</View>
</View>
)}
</Formik>
);
}
export default Form;
The error you're facing implies that the statement const fieldPhoneNumber wrapper.find('Form').dive().find('TextInput[id="fieldPhoneNumber"]'); couldn't find the TextInput component and hence the simulate function cannot be called. Try searching for the string "TextInput" inside the wrapper, and see if that works.

how to use date and time pickers in react native

I created a react native form and I want to add date and time Picker values to the form and the save them to backend. I have tried few libraries but failed.
You can use react-native-modal-datetime-picker
# using npm
$ npm i react-native-modal-datetime-picker #react-native-community/datetimepicker
# using yarn
$ yarn add react-native-modal-datetime-picker #react-native-community/datetimepicker
Example
import React, { useState } from "react";
import { Button, View } from "react-native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
const Example = () => {
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = date => {
console.warn("A date has been picked: ", date);
hideDatePicker();
};
return (
<View>
<Button title="Show Date Picker" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
/>
</View>
);
};
export default Example;
SIMPLE EXAMPLE FOR YOUR PROBLEM
import React, { Component } from "react";
import { View, Text, StyleSheet } from "react-native";
import DateTimePicker from "react-native-modal-datetime-picker";
export default class ExampleClass extends Component {
constructor(props) {
super(props);
this.state = {
isDatePickerVisible: false,
pickedDate: ""
};
}
//Date Picker handling methods
hideDatePicker = () => {
this.setState({ isDatePickerVisible: false });
};
handleDatePicked = date => {
const mdate = date.toString().split(" ");
this.setState({
pickedDate: mdate[1] + " " + mdate[2] + ", " + mdate[3]
});
this.hideDatePicker();
};
showDatePicker = () => {
this.setState({ isDatePickerVisible: true });
};
render() {
return (
<View style={styles.container}>
{/* Your View Here */}
<DateTimePicker
mode="date"
isVisible={this.state.isDatePickerVisible}
onConfirm={this.handleDatePicked}
onCancel={this.hideDatePicker}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});

Saving list with AsyncStorage

So I made a "notepad" app and I want to do so the text that the user wrote etc it should be saved, so the text doesn't get reset when user quits the app.
I'm new to react-native, after a few google searches I need AsyncStorage? to make this happen.
but really dunno on how to do it.
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
TouchableWithoutFeedback,
TouchableOpacity,
Keyboard,
AsyncStorage
} from 'react-native';
import Header from './components/header';
import ListItem from './components/listitem';
import AddList from './components/addlist';
export default function App() {
const [todos, setTodos] = useState([
]);
const pressHandler = (key) => {
setTodos((prevTodos) => {
return prevTodos.filter(todo => todo.key != key);
});
}
const submitHandler = (text) => {
if(text.length > 0) {
setTodos((prevTodos) => {
return [
{ text: text, key: Math.random().toString() },
...prevTodos
];
})
}
}
return (
<TouchableWithoutFeedback onPress={() => {
Keyboard.dismiss();
}}>
<View style={styles.container}>
<Header />
<View style={styles.content}>
<AddList submitHandler={submitHandler} />
<View style={styles.todoList}>
<FlatList
data={todos}
renderItem={({ item }) => (
<ListItem item={item} pressHandler={pressHandler} />
)}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
new problem out of nowhere worked great before now broken without touching the code
const pressHandler = key =>
setTodos(prevTodos => {
const newTodos = prevTodos.filter(todo => todo.key !== key);
storeTodosInAsync(newTodos);
console.log(prevTodos);
return newTodos;
});
const submitHandler = text => {
if (text.length > 0) {
const key = Math.random().toString();
setTodos(prevTodos => {
const newTodos = [{ text, key }, ...prevTodos];
storeTodosInAsync(newTodos);
console.log(newTodos);
return newTodos;
});
}
};
You can use AsyncStorage to store and load data to/from local storage. One thing to note is data MUST be a string, so anything like an object that is not a string needs to be stringified. You can use JSON.stringify(...) to do this. And then when you get the string back you can use JSON.parse(...) to convert it back into an object.
So to convert your current code into something that automatically loads saved todos and always saves the latest, you could write this:
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
TouchableWithoutFeedback,
TouchableOpacity,
Keyboard,
AsyncStorage,
Button
} from 'react-native';
import Header from './components/header';
import ListItem from './components/listitem';
import AddList from './components/addlist';
export default function App() {
const [todos, setTodos] = useState([]);
useEffect(() => {
restoreTodosFromAsync();
}, []);
const pressHandler = key => {
console.log('Todos BEFORE delete');
console.log(todos);
const newTodos = todos.filter(todo => todo.key !== key);
console.log('Todos AFTER delete');
console.log(todos);
setTodos(newTodos);
storeTodosInAsync(newTodos);
};
const submitHandler = text => {
if (text.length === 0) return;
const key = Math.random().toString();
console.log('Todos BEFORE submit');
console.log(todos);
const newTodos = [{ text, key }, ...todos];
console.log('Todos AFTER submit');
console.log(todos);
setTodos(newTodos);
storeTodosInAsync(newTodos);
};
const asyncStorageKey = '#todos';
const storeTodosInAsync = newTodos => {
const stringifiedTodos = JSON.stringify(newTodos);
AsyncStorage.setItem(asyncStorageKey, stringifiedTodos).catch(err => {
console.warn('Error storing todos in Async');
console.warn(err);
});
};
const restoreTodosFromAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then(stringifiedTodos => {
console.log('Restored Todos:');
console.log(stringifiedTodos);
const parsedTodos = JSON.parse(stringifiedTodos);
if (!parsedTodos || typeof parsedTodos !== 'object') return;
setTodos(parsedTodos);
})
.catch(err => {
console.warn('Error restoring todos from async');
console.warn(err);
});
};
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Header />
<View style={styles.content}>
<AddList submitHandler={submitHandler} />
<View style={styles.todoList}>
<FlatList
data={todos}
renderItem={({ item }) => <ListItem item={item} pressHandler={pressHandler} />}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}