Make react-native-paper DataTable full width - react-native

I am using the react-native-paper DataTable to create a calendar in react-native. However, i can't seem to get the table to take up 100% width, as pictured:
I have a basic setup like this:
<DataTable>
<DataTable.Row>
{weeks.map((day, index) => (
<DataTable.Cell >
<Text
key={`week-day-${index}`}
>
{dayjs(day).format("dd")}
</Text>
</DataTable.Cell>
))}
</DataTable.Row>
{weeks.map((week, weekIndex) => (
<DataTable.Row
key={`week-${weekIndex}`}
>
{week.map((day, dayIndex) => (
<DataTable.Cell
key={`day-${dayIndex}`}
>
<Text>{day.getDate()}</Text>
</DataTable.Cell>
))}
</DataTable.Row>
))}
</DataTable>
How can i make the cells take up all the available space in that row?

I'm not sure why your DataTable isnt taking up the entire width of the screen. The only reason I can imagine it not doing so is if its width is being limited by it parent components. I started working out some stuff for a calendar here:
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { View, StyleSheet, SafeAreaView } from 'react-native';
import Constants from 'expo-constants';
import { Text, DataTable,} from 'react-native-paper';
import dayjs from 'dayjs';
import TableHeader from './components/TableHeader';
import TableTitle from './components/TableTitle';
import SettingsModal from './components/SettingsModal';
import {
wrapArray,
getCalendarMonth,
WEEK_DAYS,
} from './helpers/utils';
const currentDay = dayjs();
const pickerItems = WEEK_DAYS.map((day, i) => {
return {
label: day,
value: i,
};
});
export default function App() {
// allow user to decide start of week
const [weekStart, setWeekStart] = useState(0);
const [month, setMonth] = useState(currentDay.get('month'));
const [year, setYear] = useState(currentDay.get('year'));
// when a month doesnt start at the weekStart it is necessary
// add days or blanks to put the arrays align days with the
// proper week day
const [hideExtraDays, setHideExtraDays] = useState(true);
const [days, setDays] = useState(
getCalendarMonth(month, year, weekStart, hideExtraDays)
);
const { weekDays, pickerList } = useMemo(() => {
return {
weekDays: wrapArray(WEEK_DAYS, weekStart),
pickerList: wrapArray(pickerItems, weekStart),
};
}, [weekStart]);
// incrementing/decrementing month is the same code
// accessing different dayjs functions
const incrementMonth = useCallback(
(isDecrementing) => {
const date = dayjs(`${year}-${month + 1}-01`);
const functionKey = isDecrementing ? 'subtract' : 'add';
const newDate = date[functionKey](1, 'month');
setMonth(newDate.get('month'));
setYear(newDate.get('year'));
},
[month, year]
);
// update days when these values change
useEffect(() => {
setDays(getCalendarMonth(month, year, weekStart, hideExtraDays));
}, [weekStart, month, year, hideExtraDays]);
return (
<SafeAreaView style={styles.container}>
{/*modal for calendar settings */}
<SettingsModal
{...{
pickerList,
weekStart,
setWeekStart,
hideExtraDays,
setHideExtraDays,
}}
/>
<View style={styles.calendarContainer}>
<DataTable style={{ flex: 1 }}>
<TableTitle
month={month}
year={year}
onLeftPress={() => incrementMonth(true)}
onRightPress={() => incrementMonth()}
// the month and year is a pressable that when press reveal
// dropdown flatlist picker on android this picker is unscrollable
onDateChange={(date) => {
setMonth(date.month);
setYear(date.year);
}}
/>
{/* days of the week */}
<TableHeader weekDays={weekDays} />
{days.map((week, weekIndex) => (
<DataTable.Row key={`week-${weekIndex}`}>
{week.map((day, dayIndex) => (
<DataTable.Cell key={`day-${dayIndex}`}>
{/* hideExtra days add null values when true */}
<Text>{day ? day.get('date') : null}</Text>
</DataTable.Cell>
))}
</DataTable.Row>
))}
</DataTable>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
calendarContainer: {
flex: 1,
},
});

Related

Nested lists - How to modify (delete, add, update) items from a nested list using item's buttons and useState?

My first idea was to try to delete items from a nested list. I've started using SectionList and a component that has an useState that manages the data changes of the SectionList. I'm trying to solve it using SectionList but it'd be great to display all possible alternatives (FlatList, ViewList, etc).
I figured out how to delete a whole section list (and his items) but not one item by one. I've read plenty of posts, but I did find nothing related to managing SectionList items. Maybe there's an answer out there for FlatList nested items.
Here I left an example code ready to use (without styles) based on React Native official docs:
import React, { useEffect } from "react";
import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar, Button } from "react-native";
import { useState } from "react";
const dataResource = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
n: "delete",
k: 1
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"],
n: "delete",
k: 2
},
];
function App() {
const [state, setState] = useState(dataResource)
const Item = ({ dish, obj, i}) => (
<View >
<Text >{dish} </Text>
<Button title={obj.n} onPress={() => {}} /> // add the handler
</View>
);
const SectionComponent = ({ t, k }) => (
<View>
<Text >{t} </Text>
<Button title={'section'} onPress={() => { setState(state.filter(e => e.k != k)) }} />
</View>
);
return (
<SafeAreaView >
<SectionList
sections={state}
keyExtractor={(item, index) => item + index}
renderItem={({ item, section, index}) => <Item dish={item} obj={section} i={index}/>}
renderSectionHeader={({ section: { title, k } }) => <SectionComponent k={k} t={title} />}
/>
</SafeAreaView>
);
}
export default App;
I think how you display the data is trivial. The component you use will just change how you access the data, not how you update it. What you need is helper functions for editing the data. With those in place you can do things like add/remove section items, and editing section items themselves:
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
SafeAreaView,
SectionList,
Button,
TextInput,
} from 'react-native';
const dataResource = [
{
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
id: 1,
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
id: 2,
},
];
export default function App() {
const [state, setState] = useState(dataResource);
const [sectionTitle,setSectionTitle] = useState('Drinks')
const editItem = (itemId, newValue) => {
let newState = [...state];
let itemIndex = newState.findIndex((item) => item.id == itemId);
if (itemIndex < 0) return;
newState[itemIndex] = {
...newState[itemIndex],
...newValue,
};
setState(newState);
};
const addSectionItem = (title)=>{
let newState = [...state]
newState.push({
title,
data:[],
id:newState.length+1
})
setState(newState)
}
const removeFood = (itemId, food) => {
let currentItem = state.find((item) => item.id == itemId);
console.log(currentItem);
currentItem.data = currentItem.data.filter((item) => item != food);
console.log(currentItem.data);
editItem(itemId, currentItem);
};
const addFood = (itemId, food) => {
let currentItem = state.find((item) => item.id == itemId);
console.log(currentItem.data);
currentItem.data.push(food);
console.log(currentItem.data);
editItem(itemId, currentItem);
};
const Item = ({ item, section, index }) => {
return (
<View style={styles.row}>
<Text>{item} </Text>
<Button
title={'Delete'}
onPress={() => {
removeFood(section.id, item);
}}
/>
</View>
);
};
const SectionHeader = ({ title, id }) => {
return (
<View style={styles.header}>
<Text style={{ fontSize: 18 }}>{title} </Text>
<Button
title={'X'}
onPress={() => {
setState(state.filter((e) => e.id != id));
}}
/>
</View>
);
};
const SectionFooter = ({ id }) => {
const [text, setText] = useState('');
return (
<View style={[styles.row, styles.inputWrapper]}>
<Text>Add Entry</Text>
<TextInput
value={text}
onChangeText={setText}
style={{ borderBottomWidth: 1 }}
/>
<Button title="Add" onPress={() => addFood(id, text)} />
</View>
);
};
return (
<SafeAreaView>
<Button title="Reset list" onPress={() => setState(dataResource)} />
<View style={[styles.row, styles.inputWrapper]}>
<Text>Add New Section</Text>
<TextInput
value={sectionTitle}
onChangeText={setSectionTitle}
style={{ borderBottomWidth: 1 }}
/>
<Button title="Add" onPress={() => addSectionItem(sectionTitle)} />
</View>
<View style={styles.sectionListWrapper}>
<SectionList
sections={state}
keyExtractor={(item, index) => item + index}
renderItem={Item}
renderSectionHeader={({ section }) => <SectionHeader {...section} />}
renderSectionFooter={({ section }) => <SectionFooter {...section} />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
padding: 5,
alignItems: 'center',
},
header: {
flexDirection: 'row',
paddingVertical: 5,
width: '100%',
borderBottomWidth: 1,
},
inputWrapper: {
paddingVertical: 15,
marginBottom: 10,
},
sectionListWrapper: {
padding: 5,
},
});
Here's a demo
I've improved my answer to give a clearer example of how to manipulate SectionList. An example of how to add/delete/modify sections and its data is provided.
import React from 'react';
export default function useStateHelpers(state, setState) {
const editSection = (sectionId, newValue) => {
// parse and stringify is used to clone properly
let newState = JSON.parse(JSON.stringify(state));
let itemIndex = newState.findIndex((item) => item.id == sectionId);
if (itemIndex < 0) return;
const section = {
...newState[itemIndex],
...newValue,
};
newState[itemIndex] = section;
setState(newState);
};
const editSectionDataItem = (sectionId, itemId, newValue) => {
const newState = JSON.parse(JSON.stringify(state));
const sectionIndex = newState.findIndex(
(section) => section.id == sectionId
);
const section = newState[sectionIndex];
const itemIndex = section.data.findIndex((item) => item.id == itemId);
let item = section.data[itemIndex];
section.data[itemIndex] = {
...item,
...newValue,
};
editSection(sectionId, section);
};
const editSectionTitle = (sectionId, title) =>
editSection(sectionId, { title });
const setSelectSectionItem = (sectionId, itemId, isSelected) => {
editSectionDataItem(sectionId, itemId, { isSelected });
};
const removeSectionDataItem = (sectionId, food) => {
let newState = JSON.parse(JSON.stringify(state));
let sectionIndex = newState.findIndex((section) => section.id == sectionId);
let section = newState[sectionIndex];
section.data = section.data.filter((item) => item.title != food);
editSection(sectionId, section);
};
const addSectionDataItem = (sectionId, title, price) => {
let newState = JSON.parse(JSON.stringify(state));
let sectionIndex = newState.findIndex((section) => section.id == sectionId);
let section = newState[sectionIndex];
section.data.push({
title,
price,
id: `section-${sectionId}-${section.data.length + 1}`,
});
editSection(sectionId, section);
};
const addSection = (title, data = [], id) => {
let newState = JSON.parse(JSON.stringify(state));
newState.push({
title,
data,
id: newState.length + 1,
});
setState(newState);
};
const helpers = {
editSection,
editSectionTitle,
removeSectionDataItem,
addSectionDataItem,
addSection,
setSelectSectionItem,
editSectionDataItem,
};
return helpers;
}
import React, { useState } from 'react';
import {
StyleSheet,
SafeAreaView,
View,
Button,
SectionList,
} from 'react-native';
import data from './data';
import StateContext from './StateContext';
import useStateHelpers from './hooks/useStateHelpers';
import Header from './components/SectionHeader';
import Footer from './components/SectionFooter';
import Item from './components/SectionItem';
import TextInput from './components/Input';
export default function App() {
const [state, setState] = useState(data);
const [sectionTitle, setSectionTitle] = useState('');
const helpers = useStateHelpers(state, setState);
return (
<SafeAreaView style={{ flex: 1 }}>
<StateContext.Provider value={{ state, setState, ...helpers }}>
<View style={styles.container}>
<Button title="Reset list" onPress={()=>setState(data)} />
<TextInput
label={'Add New Section'}
value={sectionTitle}
onChangeText={setSectionTitle}
onRightIconPress={() => {
helpers.addSection(sectionTitle);
setSectionTitle('');
}}
/>
<View style={styles.sectionListWrapper}>
<SectionList
style={{ flex: 1 }}
sections={state}
renderItem={(props) => <Item {...props} />}
renderSectionHeader={(props) => <Header {...props} />}
renderSectionFooter={(props) => <Footer {...props} />}
/>
</View>
</View>
</StateContext.Provider>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 8,
},
sectionListWrapper: {
padding: 5,
flex: 1,
},
});
import React, { useContext } from 'react';
import { View, TouchableOpacity, StyleSheet, Text, Button } from 'react-native';
import StateContext from '../StateContext';
export default function Item({ item, section, index }) {
const {
removeSectionDataItem,
setSelectSectionItem
} = useContext(StateContext);
const hasPrice = item.price.toString().trim().length > 0;
return (
<TouchableOpacity
style={[styles.container, item.isSelected && styles.selectedContainer]}
onPress={() =>
setSelectSectionItem(section.id, item.id, !item.isSelected)
}
>
<View style={styles.row}>
<Text>
{item.title + (hasPrice ? ' | ' : '')}
{hasPrice && <Text style={{ fontWeight: '300' }}>{item.price}</Text>}
</Text>
<Button
title={'Delete'}
onPress={() => {
removeSectionDataItem(section.id, item.title);
}}
/>
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container: {
marginVertical: 2,
},
selectedContainer: {
backgroundColor: 'rgba(0,0,0,0.2)',
},
row: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
padding: 5,
alignItems: 'center',
},
});
/*SectionFooter.js*/
import React, { useState, useContext, useEffect } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import TextInput from './Input';
import StateContext from '../StateContext';
export default function SectionFooter({ section }) {
const [title, setTitle] = useState('');
const [price, setPrice] = useState('');
const [titleWasEdited, setTitleWasEdited] = useState(false);
const { addSectionDataItem, editSectionDataItem } = useContext(StateContext);
const selectedItem = section.data.find((item) => item.isSelected);
// listen for item selection
useEffect(() => {
// pass item values to text input
setTitle(selectedItem?.title || '');
setPrice(selectedItem?.price || '');
// reset title/price input
setTitleWasEdited(false);
}, [selectedItem]);
return (
<View style={styles.container}>
<Text>
{selectedItem
? 'Editing ' + selectedItem.title
: 'Add New Entry'
}
</Text>
{/*one input handles both title and price*/}
<TextInput
label={titleWasEdited ? 'Price' : 'Title'}
value={titleWasEdited ? price : title}
onChangeText={titleWasEdited ? setPrice : setTitle}
rightIconName={titleWasEdited ? 'plus' : 'arrow-right'}
// rightIcon is disabled when theres no text
// but price is allowed to be empty
allowEmptySubmissions={titleWasEdited}
style={{ width: '80%' }}
onRightIconPress={() => {
// after title is edited allow submission
if (titleWasEdited) {
// if item was edited update it
if (selectedItem) {
editSectionDataItem(section.id, selectedItem.id, {
title,
price,
isSelected: false,
});
}
// or add new item
else {
addSectionDataItem(section.id, title, price);
}
setTitle('');
setPrice('');
}
// toggle between title & price
setTitleWasEdited((prev) => !prev);
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginVertical: 20,
alignItems: 'center',
},
});
import React, { useContext } from 'react';
import { View, StyleSheet } from 'react-native';
import { TextInput } from 'react-native-paper';
import StateContext from '../StateContext';
export default function SectionHeader({ section: { id, title } }) {
const { editTitle, setState, state } = useContext(StateContext);
return (
<View style={{ borderBottomWidth: 1 }}>
<View style={styles.header}>
<TextInput
style={styles.titleInput}
value={title}
onChangeText={(text) => {
editTitle(id, text);
}}
right={
<TextInput.Icon
name="close"
onPress={() => {
setState(state.filter((sec) => sec.id != id));
}}
/>
}
underlineColor="transparent"
activeUnderlineColor="transparent"
outlineColor="transparent"
activeOutlineColor="transparent"
selectionColor="transparent"
dense
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
header: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-around',
alignItems: 'center',
},
titleInput: {
fontSize: 18,
backgroundColor: 'transparent',
borderWidth: 0,
borderColor: 'transparent',
height: 36,
},
});

Getting undefined device.map error while displaying table react-native-table-component

I am a developing a React Native app.My requirement is to display data from API in the table format.I am using react-native-table-component.I am not able to traverse through an array to display data in the table.Below is my code:
TargetSetUpPage.js:
import React, { useEffect } from "react";
import { StyleSheet, Text, Button, TextInput, ScrollView ,View}
from "react-native";
import { useDispatch,useSelector } from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-
community/asyncstorage';
import { Table, Row, Rows } from 'react-native-table-
component';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = [useSelector(state =>
state.auth.availableDevice)];
//console.log('The devices are: '+JSON.stringify(devices));
if (typeof(devices) !== 'undefined' && devices != null) {
console.log('Not Undefined and Not Null')
} else {
console.log('Undefined or Null')
const devices = [useSelector(state =>
state.auth.availableDevice)];
}
const tableHead = ['Target Id','Target Name']
const tableRow = devices;
console.log(devices);
const tableRows = (devices || []).map(item => ({ name:
item.name
}));
useEffect(() => {
const onScreenLoad = async() => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const { userid } = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
},[dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
{/* <Table borderStyle={{borderWidth: 2, borderColor:
'#c8e1ff'}}>
<Row data={tableHead} style={styles.head} textStyle=
{styles.text}/>
<Rows data={tableRows} textStyle={styles.text}/>
</Table> */}
<FlatList
data={devices}
keyExtractor={item => item.TargetId}
renderItem={itemData => (
<View style={styles.card}>
{/* <Text style={styles.text}>{itemData.item.TargetName}
</Text> */}
<Button title={itemData.item.TargetName} onPress={()=>{}}/>
</View>
)}
numColumns={2}
/>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button
title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},});
export default TargetSetUpPage;
After executing it is showing the below tableRow array in console:
Not Undefined and Not Null
Array [
undefined,
]
TypeError: undefined is not an object (evaluating 'item.name')
But If I remove Table and display FlatList,I am able to display the list without any problem .I dont know what mistake I am doing in displaying the Table.Thanks in Advance.
// devices will be the same as auth.availableDevice in your redux
// If auth.availableDevice is undefined at any point, like during boot
// then devices will be undefined too
const devices = useSelector(state => state.auth.availableDevice);
// Let's write our map code so that it handles the case where devices is undefined
const tableRows = (devices || []).map(item => ({ name: item.name }));
I used DataTable from react-native-paper to get the table.Below is the code that renders data from API.
TargetSetUpPage.js:
import React, {useEffect} from "react";
import {StyleSheet, Text, Button, TextInput, ScrollView, View, FlatList}
from "react-native";
import {useDispatch, useSelector} from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-community/async-storage';
import {DataTable} from 'react-native-paper';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = useSelector(state => state.auth.availableDevice);
useEffect(() => {
const onScreenLoad = async () => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const {userid} = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
}, [dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
<DataTable>
<DataTable.Header>
<DataTable.Title>
<Text>Target Id</Text>
</DataTable.Title>
<DataTable.Title>
<Text>Target Name</Text>
</DataTable.Title>
</DataTable.Header>
{devices.map((item, key) => (
<DataTable.Row>
<DataTable.Cell>{item.TargetId}</DataTable.Cell>
<DataTable.Cell>{item.TargetName}</DataTable.Cell>
</DataTable.Row>
)
)}
</DataTable>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},
});
export default TargetSetUpPage;
which displays the table on the screen as shown below.

React Native useState not auto Updating?

why useState auto update? I'll press button and not showing textinput. but I can save file without change. textinput will be showing. sorry my bad english
import React, { useState,useEffect } from 'react';
import {Text, TextInput, View, Button,} from 'react-native';
const Test = ({navigation}) => {
const [textInput, settextInput] = useState([]);
useEffect(() => {
addTextInput = (key) => {
textInput.push([<TextInput style={{backgroundColor :'#7ACB4A',marginTop:10}} key={key} />]);
settextInput(textInput);
console.log(textInput);
}
},[textInput]);
return(
<View>
<Button title='+' onPress={() =>
addTextInput(textInput.length)} />
{textInput.map((value, index) => {
return value
})}
<Text>{textInput.length}</Text>
</View>
);
}
export default Test;
I have a few suggests to make your code better.
Don't change state value if not in use 'setState'.
This is false by nature and causes errors.
addTextInput = (key) => {
textInput.push([<TextInput style={{backgroundColor :'#7ACB4A',marginTop:10}} key={key} />]);
settextInput(textInput);
console.log(textInput);
}
State merely contains value, it should not contain different things. You should return TextInput in your map function.
I try rewrite your code, sorry because my english. Hope help you
code:
const [textInput, setTextInput] = React.useState(['1', '2'])
const addTextInput = (key: string) => {
const tempInput = textInput.concat([key])
setTextInput(tempInput)
}
return (
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Button title="+" onPress={() => addTextInput(textInput.length.toString())} />
{textInput.map((value, index) => {
return (
<TextInput style={{ backgroundColor: '#7ACB4A', marginTop: 10, width: '70%' }} key={index + ''} />
)
})}
<Text>{textInput.length}</Text>
</View>
)
}

Display a view when another view is being focused?

I have a TextInput, when the text is onFocus, I want to display a DateTimePicker component.
I have a AddMeeting.js component, which contains the TextInput, and another component for the DateTimePicker itself.
I thought about having a state (boolean) in AddMeeting that would change when TextInput is getting onFocus / onBlur and with conditional rendering to display / dismiss the DateTimePicker from the screen.
the problem is, I already have a show and hide functions in the DateTimePicker component itself, and it seems redundant to have the state in AddMeeting too.
This is the DateimePicker:
import React, { useState } from "react";
import { View } from "react-native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
const CustomDateTimePicker = () => {
const [isPickerVisible, setPickerVisibility] = useState(false);
const [chosenDate, setChosenDate] = useState("");
const showPicker = () => {
setPickerVisibility(true);
};
const hidePicker = () => {
setPickerVisibility(false);
};
const handleConfirm = (date) => {
setChosenDate = date
console.warn("A date has been picked: ", date);
hidePicker();
}
return (
<View>
<DateTimePickerModal
isVisible={isPickerVisible}
mode="datetime"
onConfirm={handleConfirm}
onCancel={hidePicker}
/>
</View>
)
}
export default CustomDateTimePicker;
This is the AddMeeting.js:
const AddMeetingScreen = ({ navigation }) => {
_popAddMeeting = () => {
navigation.pop()
}
_showPicker = () => {
console.log("SHOWING PICKER");
}
return (
<>
<SafeAreaView style={styles.addMeetingContainer}>
<View style={styles.headerContainer}>
<TouchableOpacity onPress={_popAddMeeting} style={styles.backButtonTouchable}>
<Icon name="arrow-back-outline" size={28} color="#000" />
</TouchableOpacity>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.title}>
New Meeting
</Text>
</View>
</View>
<View style={{ marginTop: 32 }}>
<MinimalTextInput title="When" placeholder="test" onFocus={_showPicker} />
</View>
</SafeAreaView>
</>
)
}
}

react-native-community/datetimepicker is not setting all its values

I am doing a datatimepicker with #react-native-community/datetimepicker but I am facing a problem in which the default value does not change its year and day but It changes its hour.
I did a logic to open the set day and year, and after closing/setting that it opens the set hour, it is weird because when I set the year and day It changes its value in the screen but when I finished changing the hour, they set its value to default
const [date, setDate] = useState(new Date());
const [show, setShow] = useState(false);
const [mode, setMode] = useState('date');
[enter image description here][1]
const initialState = {
guests,
smoking,
date,
show,
mode
};
const changeShowMode = () => {
setShow(true)
setMode('date')
}
const handleReservation = () => {
console.log(JSON.stringify(initialState));
setGuests(1)
setSmoking(false)
setDate(date)
}
<View style={styles.formRow}>
<Text style={styles.formLabel}>Date and Time</Text>
<TouchableOpacity style={styles.formItem}
style={{
padding: 7,
borderColor: '#512DA8',
borderWidth: 2,
flexDirection: 'row'
}}
onPress={() => changeShowMode()}
>
<Icon type='font-awesome' name='calendar' color='#512DA8' />
<Text>
{' ' + Moment(date).format('DD-MMM-YYYY h:mm A') }
</Text>
</TouchableOpacity>
{show && (
<DateTimePicker
value={date}
display='default'
mode={mode}
minimumDate={new Date()}
minuteInterval={30}
onChange={(event, date) => {
if (date === undefined) {
setShow(false)
}
else {
setShow(mode === 'time' ? false : true),
setMode('time'),
setDate(date)
}
}}
/>
)}
</View>
<View style={styles.formRow}>
<Button
title='reserver'
color='#512DA8'
onPress={() => handleReservation()}
accessibilityLabel='Learn more about this purple button'
/>
</View>
You will see how the hour is changing, but the year and day are not.
https://i.stack.imgur.com/2FfuI.jpg
Your logic seems OK but there is something happening under the hood when you update the state. I have been playing with this and set up a snack for you. Here is the link.
Full code here
import * as React from 'react';
import {
Text,
View,
StyleSheet,
Button,
TouchableOpacity,
Alert,
} from 'react-native';
import Constants from 'expo-constants';
import Moment from 'moment';
import DateTimePicker from '#react-native-community/datetimepicker';
export default function App() {
const [dateInfo, updateDateInfo] = React.useState({
date: new Date(),
show: false,
mode: 'date',
});
const changeShowMode = () => {
updateInfo({ show: true, mode: 'date' });
};
const updateInfo = (info) => {
updateDateInfo({ ...dateInfo, ...info });
};
const handleReservation = () => {};
return (
<>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Date and Time</Text>
<TouchableOpacity
style={styles.formItem}
onPress={() => changeShowMode()}>
<Text>
{' ' + Moment(dateInfo.date).format('DD-MMM-YYYY h:mm A')}
</Text>
</TouchableOpacity>
{dateInfo.show && (
<DateTimePicker
value={dateInfo.date}
display="default"
mode={dateInfo.mode}
minimumDate={new Date()}
minuteInterval={30}
onChange={(event, dateTime) => {
if (dateTime === undefined) {
updateInfo({ show: false });
} else if (dateInfo.mode === 'time') {
let timeDate = Moment(dateTime);
updateInfo({
show: false,
mode: 'date',
date: Moment(dateInfo.date)
.set('hour', timeDate.get('hour'))
.set('minute', timeDate.get('minute'))
.set('minute', timeDate.get('second'))
.toDate(),
});
} else {
updateInfo({ mode: 'time', date: dateTime });
}
}}
/>
)}
</View>
<View style={styles.formRow}>
<Button
title="reserver"
color="#512DA8"
onPress={() => handleReservation()}
accessibilityLabel="Learn more about this purple button"
/>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Basically instead of updating states multiple time, which were causing an issue, I used an object which updates the date object fine. Please take a look and try if this works fine.
Good Luck