I have an application that queries the stock exchange in real time, but when I open the application the first time it does not update any displayed value, as if it were hardcoded, but when I save the file (ctrl + S) it starts to update normally , Can anyone help me with this?
description code below:
this function is listening the data on firebase;
ticketRT is my state that is not updating at first time
Object.keys(subscriptionStocks).forEach((stock) => {
if(!subscriptionStocks[stock].listener) {
firebase.database().ref(`${endPointStocks}/${stock}`).on('value', async data => {
let stock = data.val();
if (stock) {
stockArray[stock.ticket] = stock;
setTicketRT(stockArray);
await updateListStocks(stock);
} else {
console.log(`[listenerStock]: Não foi possível cadastrar o ativo "${stock}"`);
}
});
subscriptionStocks[stock].listener = true;
this function updates the stock list
let tmpStock = {};
console.log('LENGTH 11111', stockArray.length);
if (stockArray.length === 0) {
console.log('LENGTH 22222 ≥≥≥ 2', stockArray.length);
tmpStock[stock.ticket] = stock;
stockArray.push(tmpStock);
setTicketRT(stockArray);
updateWalletBalance();
} else {
const foundStockInArray = stockArray.some(
el => {
let inStock = el[Object.keys(el)[0]];
return inStock.ticket == stock.ticket;
}
);
if (foundStockInArray) {
updateStock(stock);
} else {
tmpStock[stock.ticket] = stock;
stockArray.push(tmpStock);
setTicketRT(stockArray);
updateWalletBalance();
}
}
this is my FlatList that is not updating
<FlatList
style={styles.actionList}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
data={ticketRT}
extraData={(item, index) => {
if(ticketRT !== undefined) {
let stocks = ticketRT[index];
let stock = stocks[Object.keys(stocks)[0]];
return stock.ticket;
}
}}
keyExtractor={(item, index ) => {
if(ticketRT !== undefined) {
let stocks = ticketRT[index];
let stock = stocks[Object.keys(stocks)[0]];
return stock.ticket;
}
}}
renderItem={({ index }) => {
let stocks = ticketRT[index];
let stock = stocks[Object.keys(stocks)[0]];
return <Ativos data={stock} />
}}
contentInset={{ bottom: 10, top: -5 }}
/>
ALL CODE IS FOUNDED HERE: https://gist.github.com/paulorod07/4e74976345a6a68b0e32755cf5ae1c46
I'm using default example of NormalPeoplePicker from https://developer.microsoft.com/en-us/fluentui#/controls/web/peoplepicker#IPeoplePickerProps.
When the dropdown displays it cuts off longer items (example: 'Anny Lundqvist, Junior Manager of Soft..'). How do I make it wider, so that the full item's text displays?
import * as React from 'react';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { IPersonaProps } from 'office-ui-fabric-react/lib/Persona';
import { IBasePickerSuggestionsProps, NormalPeoplePicker, ValidationState } from 'office-ui-fabric-react/lib/Pickers';
import { people, mru } from '#uifabric/example-data';
const suggestionProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: 'Suggested People',
mostRecentlyUsedHeaderText: 'Suggested Contacts',
noResultsFoundText: 'No results found',
loadingText: 'Loading',
showRemoveButtons: true,
suggestionsAvailableAlertText: 'People Picker Suggestions available',
suggestionsContainerAriaLabel: 'Suggested contacts',
};
const checkboxStyles = {
root: {
marginTop: 10,
},
};
export const PeoplePickerNormalExample: React.FunctionComponent = () => {
const [delayResults, setDelayResults] = React.useState(false);
const [isPickerDisabled, setIsPickerDisabled] = React.useState(false);
const [mostRecentlyUsed, setMostRecentlyUsed] = React.useState<IPersonaProps[]>(mru);
const [peopleList, setPeopleList] = React.useState<IPersonaProps[]>(people);
const picker = React.useRef(null);
const onFilterChanged = (
filterText: string,
currentPersonas: IPersonaProps[],
limitResults?: number,
): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (filterText) {
let filteredPersonas: IPersonaProps[] = filterPersonasByText(filterText);
filteredPersonas = removeDuplicates(filteredPersonas, currentPersonas);
filteredPersonas = limitResults ? filteredPersonas.slice(0, limitResults) : filteredPersonas;
return filterPromise(filteredPersonas);
} else {
return [];
}
};
const filterPersonasByText = (filterText: string): IPersonaProps[] => {
return peopleList.filter(item => doesTextStartWith(item.text as string, filterText));
};
const filterPromise = (personasToReturn: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (delayResults) {
return convertResultsToPromise(personasToReturn);
} else {
return personasToReturn;
}
};
const returnMostRecentlyUsed = (currentPersonas: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
return filterPromise(removeDuplicates(mostRecentlyUsed, currentPersonas));
};
const onRemoveSuggestion = (item: IPersonaProps): void => {
const indexPeopleList: number = peopleList.indexOf(item);
const indexMostRecentlyUsed: number = mostRecentlyUsed.indexOf(item);
if (indexPeopleList >= 0) {
const newPeople: IPersonaProps[] = peopleList
.slice(0, indexPeopleList)
.concat(peopleList.slice(indexPeopleList + 1));
setPeopleList(newPeople);
}
if (indexMostRecentlyUsed >= 0) {
const newSuggestedPeople: IPersonaProps[] = mostRecentlyUsed
.slice(0, indexMostRecentlyUsed)
.concat(mostRecentlyUsed.slice(indexMostRecentlyUsed + 1));
setMostRecentlyUsed(newSuggestedPeople);
}
};
const onDisabledButtonClick = (): void => {
setIsPickerDisabled(!isPickerDisabled);
};
const onToggleDelayResultsChange = (): void => {
setDelayResults(!delayResults);
};
return (
<div>
<NormalPeoplePicker
// eslint-disable-next-line react/jsx-no-bind
onResolveSuggestions={onFilterChanged}
// eslint-disable-next-line react/jsx-no-bind
onEmptyInputFocus={returnMostRecentlyUsed}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={suggestionProps}
className={'ms-PeoplePicker'}
key={'normal'}
// eslint-disable-next-line react/jsx-no-bind
onRemoveSuggestion={onRemoveSuggestion}
onValidateInput={validateInput}
removeButtonAriaLabel={'Remove'}
inputProps={{
onBlur: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onBlur called'),
onFocus: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onFocus called'),
'aria-label': 'People Picker',
}}
componentRef={picker}
onInputChange={onInputChange}
resolveDelay={300}
disabled={isPickerDisabled}
/>
<Checkbox
label="Disable People Picker"
checked={isPickerDisabled}
// eslint-disable-next-line react/jsx-no-bind
onChange={onDisabledButtonClick}
styles={checkboxStyles}
/>
<Checkbox
label="Delay Suggestion Results"
defaultChecked={delayResults}
// eslint-disable-next-line react/jsx-no-bind
onChange={onToggleDelayResultsChange}
styles={checkboxStyles}
/>
</div>
);
};
function doesTextStartWith(text: string, filterText: string): boolean {
return text.toLowerCase().indexOf(filterText.toLowerCase()) === 0;
}
function removeDuplicates(personas: IPersonaProps[], possibleDupes: IPersonaProps[]) {
return personas.filter(persona => !listContainsPersona(persona, possibleDupes));
}
function listContainsPersona(persona: IPersonaProps, personas: IPersonaProps[]) {
if (!personas || !personas.length || personas.length === 0) {
return false;
}
return personas.filter(item => item.text === persona.text).length > 0;
}
function convertResultsToPromise(results: IPersonaProps[]): Promise<IPersonaProps[]> {
return new Promise<IPersonaProps[]>((resolve, reject) => setTimeout(() => resolve(results), 2000));
}
function getTextFromItem(persona: IPersonaProps): string {
return persona.text as string;
}
function validateInput(input: string): ValidationState {
if (input.indexOf('#') !== -1) {
return ValidationState.valid;
} else if (input.length > 1) {
return ValidationState.warning;
} else {
return ValidationState.invalid;
}
}
/**
* Takes in the picker input and modifies it in whichever way
* the caller wants, i.e. parsing entries copied from Outlook (sample
* input: "Aaron Reid <aaron>").
*
* #param input The text entered into the picker.
*/
function onInputChange(input: string): string {
const outlookRegEx = /<.*>/g;
const emailAddress = outlookRegEx.exec(input);
if (emailAddress && emailAddress[0]) {
return emailAddress[0].substring(1, emailAddress[0].length - 1);
}
return input;
}
Component which renders suggestion list have fixed width of 180px. Take a look at PeoplePickerItemSuggestion.styles.ts.
What you can do is to modify this class .ms-PeoplePicker-Persona:
.ms-PeoplePicker-Persona {
width: 260px; // Or what ever you want
}
UPDATE - Solution from comments
Change width trough styles property of PeoplePickerItemSuggestion Component
const onRenderSuggestionsItem = (personaProps, suggestionsProps) => (
<PeoplePickerItemSuggestion
personaProps={personaProps}
suggestionsProps={suggestionsProps}
styles={{ personaWrapper: { width: '100%' }}}
/>
);
<NormalPeoplePicker
onRenderSuggestionsItem={onRenderSuggestionsItem}
pickerCalloutProps={{ calloutWidth: 500 }}
...restProps
/>
Working Codepen example
For more information how to customize components read Component Styling.
Good afternoon folks,
Here's an issue that's currently driving me up the wall and I cannot seem to see the wood for the trees.
I have a React Native app with a built in file uploader, part of this uploader is the use of the abortcontroller which allows the code to send a signal to the fetch request to stop the call in-flight
This works perfectly as one would expect the problem is if the user then selects another file or tries to upload the file the cancelled previously my promise returns instantly with the abort error still in place preventing any further uploads and for love nor money can I seem to find a way to stop this from occurring.
Here is the reduced version of my screen (urls removed, some data points removed, etc) to protect the privacy of my system
import React from 'react';
import {StyleSheet,View,ScrollView,Alert,} from 'react-native';
import AppSettings from '../../constants/AppSettings'
import Colours from '../../constants/Colours';
import CustomHeader from '../../components/CustomHeader';
import CustomButton from '../../components/CustomButton';
import TextContent from '../../components/TextContent';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Constants from 'expo-constants';
import * as DocumentPicker from 'expo-document-picker';
import * as Permissions from 'expo-permissions';
import CustomInput from '../../components/CustomInput';
import Progressbar from '../../components/ProgressBar';
import * as Network from 'expo-network';
export default class UploadScreen extends React.Component {
state = {
file: null,
filetype: null,
fileExtention:null,
uploading: false,
pickResult: null,
mainDataLoaded: false,
docDesc:null,
enteredPassword:'',
currentUploadPercent:0,
uploadTime: 0,
startPick: false,
};
render() {
return (
<View style={styles.screenContainer}>
<LinearGradient start={[0, 1]} end={[0,0.9]} colors={['rgba(163, 163, 163,1)', 'rgba(163, 163, 163,0)']} style={{flex:1}} >
<ScrollView style={styles.scrollContainer} contentContainerStyle={styles.defaultContainer}>
<View style={{flex:1, alignItems: 'center', justifyContent: 'center', width:'100%' }}>
<TextContent>Upload file containing: {this.state.docDesc}</TextContent>
{this._maybeRenderFile()}
{this._maybeRenderControls()}
{this._maybeRenderUploadingIndicator()}
</View>
</ScrollView>
</LinearGradient>
</View>
);
}
_maybeRenderUploadingIndicator = () => {
if (this.state.uploading) {
return (
<View style={{width:'80%',alignItems:'center'}}>
<Progressbar progress={this.state.currentUploadPercent}/>
<CustomButton style={{width:'100%'}} onPress={()=>{AbortUpload(this)}} title='Cancel Upload'></CustomButton>
</View>
);
}
};
_maybeRenderControls = () => {
if (!this.state.uploading) {
return (
<View style={{width:'100%',alignItems:'center'}}>
<CustomButton style={{width:'80%'}} onPress={this._pickImage} title='Select file to upload'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="folder-open" size={30}/></CustomButton>
</View>
);
}
};
_maybeRenderFile = () => {
if (this.state.file) {
switch (this.state.filetype) {
case 'application/pdf':
const passwordHandler = enteredText => {
this.setState({enteredPassword: enteredText});
};
return (
<View style={{alignItems:'center'}}>
<MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-pdf" size={100}/>
<TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
<TextContent>If this file requires a password to access please type it below or leave blank if not required.</TextContent>
{!this.state.uploading && (
<View>
<CustomInput placeholder='PDF Password (if applicable)' autoCapitalize='characters' autoCompleteType='off' autoCorrect={false} textContentType='none' onChangeText={passwordHandler} value={this.state.enteredPassword}/>
<CustomButton style={{width:'100%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
<TextContent style={{textAlign:'center'}}>Or</TextContent>
</View>
)}
</View>
);
break;
case 'image/jpg':
case 'image/png':
case 'image/gif':
return (
<View style={{alignItems:'center'}}>
<MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-image" size={100}/>
<TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
{!this.state.uploading && (
<View>
<CustomButton style={{minWidth:'80%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
<TextContent style={{textAlign:'center'}}>Or</TextContent>
</View>
)}
</View>
);
break;
default:
break;
}
}
};
_askPermission = async (type, failureMessage) => {
const { status, permissions } = await Permissions.askAsync(type);
if (status === 'denied') {
alert(failureMessage);
}
};
_pickImage = async () => {
await this._askPermission(
Permissions.CAMERA_ROLL,
'We need the file permission to access files from your phone...'
);
if(!this.state.startPick){
this.setState({startPick: true})
let pickerResult = await DocumentPicker.getDocumentAsync({});
if(pickerResult.type == 'success'){
this.setState({startPick: false})
//Get file extention
var splitAt = pickerResult.name.lastIndexOf(".")
var fileExt = pickerResult.name.slice(splitAt,pickerResult.name.length).toLowerCase()
switch (fileExt) {
case '.pdf':
this.setState({file: pickerResult.name, filetype: 'application/pdf', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.jpg':
this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.jpeg':
this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.png':
this.setState({file: pickerResult.name, filetype: 'image/png', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.gif':
this.setState({file: pickerResult.name, filetype: 'image/gif', pickResult: pickerResult, fileExtention: fileExt})
break;
default:
this.setState({file: null, filetype: null, pickResult: null})
Alert.alert('Unsupported filetype','For security reasons you may only select images or PDF files to upload.')
break;
}
}else{
//No file selected
this.setState({file: null, filetype: null, pickResult: null})
this.setState({startPick: false})
}
if(__DEV__){console.log('Result:', pickerResult)}
}else{
if(__DEV__){console.log('Pick already started')}
}
};
_StatusCheck = async() =>{
return fetch('Url for server side upload status response')
.then((response) => response.json())
.then((responseJson) => {
return responseJson
})
.catch((error) =>{
console.error(error);
});
}
_handleImagePicked = async pickerResult => {
try {
if (!pickerResult.cancelled) {
var thisTime = Date.now()
this.setState({uploadTime: thisTime})
var myPromise = new Promise(function(){})
myPromise = MakeQuerablePromise(new uploadFileAsync(
pickerResult.uri,
this.state.docType + '-' + Date.now() + this.state.fileExtention,
this.state.filetype,
this.state.docType,
this.state.docDesc,
this.state.enteredPassword,
this.state.quoteID,
this.state.uploading,
thisTime,
controller.signal
));
this.setState({ uploading: true });
if(__DEV__){
console.log("Initial fulfilled:", myPromise.isFulfilled());//false
console.log("Initial rejected:", myPromise.isRejected());//false
console.log("Initial pending:", myPromise.isPending());//true
}
for (let index = 0; index < 1;) {
var currentStatus = await this._StatusCheck()
var curTime = new Date()
if(__DEV__){
console.log('Time:',curTime.getHours(),':',curTime.getMinutes(),':',curTime.getSeconds())
console.log('Status:',currentStatus)
console.log("Promise status- fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
console.log('Promise Content:',myPromise)
}
if(!myPromise.isRejected()){
if(currentStatus.percent != undefined){
if(currentStatus.percent < 100){
this.setState({currentUploadPercent:currentStatus.percent})
if(__DEV__){
console.log('Upload progess ' + currentStatus.percent)
console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
}else{
this.setState({currentUploadPercent:currentStatus.percent})
if(__DEV__){
console.log('Upload progess 100%')
console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
}
}
}
if(myPromise.isFulfilled() == true){
if(__DEV__){
console.log("Entered Fulfilled State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
index++
}
if(myPromise.isRejected() == true){
if(__DEV__){
console.log("Entered Rejected State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
index++
}
}
if(myPromise.isRejected() == false){
myPromise.then(response => response.json()).then((responseJson)=>{
if(__DEV__){
console.log('Promise Json:',responseJson)
console.log("Final fulfilled:", myPromise.isFulfilled());//true
console.log("Final rejected:", myPromise.isRejected());//false
console.log("Final pending:", myPromise.isPending());//false
}
if(responseJson.datapage.result.gitUploadStatus.successful == true){
//Successful upload
this.props.navigation.navigate('CaptureThanks')
}else{
//Upload had a issue
Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
}
})
}else{
//Rejected promise handle failure
if(myPromise.rejectReason() == 'AbortError'){
myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
resolve('AbortError')
}))
}else{
Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
}
}
}
} catch (e) {
if(__DEV__){
console.log('Error Name:',e.name)
console.log('Catch Error:',{ e });
}
return;
} finally {
if(__DEV__){
console.log('Reached Final')
}
myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
resolve('Finished')
}))
console.log(myPromise)
this.setState({ uploading: false, currentUploadPercent:0 });
}
};
}
function MakeQuerablePromise(promise) {
// Don't modify any promise that has been already modified.
if (promise.isResolved){
return promise
};
// Set initial state
var isPending = true;
var isRejected = false;
var isFulfilled = false;
var rejectReason = '';
// Observe the promise, saving the fulfillment in a closure scope.
var result = promise.then(
function(v) {
isFulfilled = true;
isPending = false;
rejectReason = '';
return v;
},
function(e) {
isRejected = true;
isPending = false;
rejectReason = e.name;
return e;
}
);
result.isFulfilled = function() { return isFulfilled; };
result.isPending = function() { return isPending; };
result.isRejected = function() { return isRejected; };
result.rejectReason = function() {return rejectReason; };
return result;
}
const controller = new AbortController;
async function uploadFileAsync(uri,name,type,docType,docDesc,password,quoteid,isUploading,uploadTime,abortSignal) {
if(!isUploading){
if(__DEV__){console.log('Making upload request for ',docType,' Document description:', docDesc)}
let apiUrl = 'Url to push the upload to';
let formData = new FormData();
//(method) FormData.append(name: string, value: string | Blob, fileName?: string): void
formData.append('filename', {
uri,
name: name,
type: type,
documentType:docType,
description:docDesc,
password:password,
quoteid:quoteid,
});
let options = {
method: 'POST',
body: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
signal: abortSignal,
};
if(__DEV__){console.log('Options:', options)}
return fetch(apiUrl, options);
}else{
return null
}
}
async function AbortUpload(stateObject){
controller.abort()
stateObject.setState({isUploading: false})
}
UploadScreen.navigationOptions = {
header: () => <CustomHeader goback={true} title='Document Upload'/>,
title: AppSettings.AppName + ' ',
headerTitleStyle:{
fontFamily: AppSettings.HeaderFont,
},
headerStyle: {
backgroundColor: Colours.HeaderBackground
},
headerTintColor: Colours.HeaderText
};
const styles = StyleSheet.create({
screenContainer:{
flex:1,
backgroundColor: Colours.PrimaryBackgroud,
},
scrollContainer:{
flex: 1,
height:'100%'
},
defaultContainer: {
alignItems: 'center',
},
});
Forgive the nature of my code as I'm still pretty new to react native and only been doing it for a few months now so still getting my head around a lot of the features and systems and only just updated to the latest version of expo (36) yesterday so I could enable the fetch abort.
But it anyone has any clues as to why it seems that after the signal is called once to abort every future request then seems to be getting that same signal regardless that the user has not clicked it again and I am not storing it in state so I can't seem to understand why its persisting to the degree that I even went to the extreme of rebuilding the promises at start and end to ensure they were cleaned every time the upload is fired.
You shared way too much code, but I'll have a try at this.
First of all, I'm surprised it even worked in the first place. You're creating your abort controller like so:
const controller = new AbortController;
While it should be:
const controller = new AbortController();
Now, when it comes to the logic, I believe it's displaying an error because you're using that same controller, which was already canceled.
The trick would be to turn it into a state and update it with a new AbortController as soon as it gets aborted (probably in your AbortUpload method, right after calling controller.abort()).
I am trying to enable date range using react-native-calendars. On my app, the calendar loads a few 'markedDates'; now I need to implement a start and end date functionality without affecting these initial dates. Unfortunately, I am struggling to achieve that. Any ideas on how can I do that?
Thank you in advance.
Pseudo-code
Load calendar with marked dates
Tap on start date
Tap on end date
Continue
Component
export default class Dates extends Component {
static navigationOptions = {
title: 'Choose dates',
}
constructor(props) {
super(props)
this.state = {
selected: undefined,
marked: undefined,
}
}
componentDidMount() {
this._markDate()
}
_markDate = () => {
nextDay = []
const marked = {
[nextDay]: { selected: true, marked: true },
}
Util._findShows(resp => {
resp.map(data => {
nextDay.push(data.date)
})
var obj = nextDay.reduce((c, v) => Object.assign(c, { [v]: { marked: true, dotColor: 'black' } }), {})
this.setState({ marked: obj })
})
}
_selectDate = obj => {
this.setState({ selected: obj.dateString })
}
render() {
return (
<View style={styles.container}>
<CalendarList
// Callback which gets executed when visible months change in scroll view. Default = undefined
onVisibleMonthsChange={months => {
console.log('now these months are visible', months)
}}
// Max amount of months allowed to scroll to the past. Default = 50
pastScrollRange={0}
// Max amount of months allowed to scroll to the future. Default = 50
futureScrollRange={12}
// Enable or disable scrolling of calendar list
scrollEnabled={true}
// Enable or disable vertical scroll indicator. Default = false
showScrollIndicator={true}
markedDates={
// [this.state.selected]: { selected: true, disableTouchEvent: true, selectedDotColor: 'orange' },
this.state.marked
}
onDayPress={day => {
this._selectDate(day)
}}
/>
<View style={styles.ctaArea}>
<TouchableOpacity style={styles.button} onPress={() => this.props.navigation.navigate('Dates')}>
<Text style={styles.btTitle}>Continue</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
I had the same struggle but so I decided to make my own version.
I'm sure it can be done better and have more functionalities but it works okay for me
const [dates, setDates] = useState({});
// get array of all the dates between start and end dates
var getDaysArray = function(start, end) {
for (var arr=[], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)){
arr.push(useFormatDate(new Date(dt)));
}
return arr;
};
// empty object
const emptyObj = (obj: Object) => {
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
delete dates[props[i]];
}
}
// check if first date is smaller or equals to second date
const compareDate = function(first: string, second: string) {
return (new Date(first) <= new Date(second) ? true : false);
}
// fill with color the date between first and second date selected
const fillRangeDate = function(first: string, second: string) {
emptyObj(dates);
let newDates = dates;
newDates[first]={selected: true, Colors[colorScheme].tint};
newDates[second]={selected: true, color: Colors[colorScheme].tint};
var range = getDaysArray(first, second);
for (var i = 1; i < range.length - 1; i++)
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
setDates({...newDates})
}
const selectDate = (day) => {
let selectedDate = day.dateString;
let newDates = dates;
// if 2 dates are selected
if (Object.keys(dates).length >= 2) {
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], selectedDate)) {
fillRangeDate(props[0], selectedDate);
} else {
emptyObj(dates);
}
} else {
// 1 date selected
if (Object.keys(dates).length == 0) {
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
} else if (Object.keys(dates).length == 1) { // 2 dates selected
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
// if 2nd date < 1st date, cancel range
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], props[1])) {
var range = getDaysArray(props[0], props[1]);
for (var i = 1; i < range.length - 1; i++) {
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
}
} else {
emptyObj(dates);
}
}
}
setDates({...newDates})
}
You'll also need to add this function that I implemented as a hook:
const useFormatDate = (date: Date) => {
function padTo2Digits(num) {
return num.toString().padStart(2, '0');
}
return [
date.getFullYear(),
padTo2Digits(date.getMonth() + 1),
padTo2Digits(date.getDate()),
].join('-');
};
Help me to improve this code and maybe create a merge request on the wix/react-native-calendar
Hope this helps