Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to update markedDates from onDayPress event #283

Closed
francisrod01 opened this issue Dec 27, 2017 · 11 comments
Closed

How to update markedDates from onDayPress event #283

francisrod01 opened this issue Dec 27, 2017 · 11 comments

Comments

@francisrod01
Copy link

francisrod01 commented Dec 27, 2017

Description

So... how to toggle (mark/unmark) dates when onDayPress was called?

Some like this:

Expected Behavior

I tried to do this with Object.keys(), push and splice but markedDates haven't a index to manipulate it, so it's very difficult to me to do this.

Observed Behavior

Date marking

!Disclaimer! Make sure that markedDates param is immutable. If you change markedDates object content but the reference to it does not change calendar update will not be triggered.

Environment

  • yarn info react-native-calendars: {latest: '1.16.1'}

  • yarn info react-native: {latest: '0.51.0'}

  1. Phone/emulator/simulator & version: Genymotion 2.11.0

Reproducible Demo

The same code can to see below:

some imports:

import React, { Component } from 'react'
import moment from 'moment'
import { ScrollView, View, Text } from 'react-native'
import { Calendar } from 'react-native-calendars'

defining constants:

const _format = 'YYYY-MM-DD'
const _today = moment().format(_format)
const _maxDate = moment().add(15, 'days').format(_format)
// It is not possible to reserve some to current day.
let _markedDates = {
    [_today]: {disabled: true}
}

onDayPress method called:

const onDaySelect = (day) => {
    const selectedDay = moment(day.dateString).format(_format)

    const arrDates = Object.keys(_markedDates)
        // .map((value, id, ssss) => ({id, value}))
        .map((d, i) => {

            console.log('d, i: ', _markedDates.indexOf(i))
            
            // const markedDay = moment(d.value).format(_format)
            // if (_today !== markedDay) {
                // console.log('push: ', markedDay)
            // } else if () {
                // console.log('splice: ', d, i)
            // }
        })

    // console.log('array dates: ', arrDates)
}

and the calendar component below:

const WixCalendar = (props) => {
    return (
        <Calendar
            // theme={{
            //     selectedDayBackgroundColor: 'steelblue',
            //     dotColor: '#00adf5',
            // }}
            
            // we use moment.js to give the minimum and maximum dates.
            minDate={_today}
            maxDate={_maxDate}

            // hideArrows={true}

            onDayPress={(day) => onDaySelect(day)}
            markedDates={_markedDates}
        />
    )
}

export default WixCalendar

EDIT:

I've been working to improved this with clean JS:

https://codepen.io/francisrod01/pen/RxKQeO

image

I clicked in day 31 but the day doesn't marked in the calendar.

@eddiegroves
Copy link

You can track the marked dates as state in a stateful React component, here's an example I've based of your expo example:

import React from 'react'
import moment from 'moment' // 2.20.1
import { View } from 'react-native' // 0.0.1
import { Calendar } from 'react-native-calendars' // 1.16.1


const _format = 'YYYY-MM-DD'
const _today = moment().format(_format)
const _maxDate = moment().add(15, 'days').format(_format)

class WixCalendar extends React.Component {
  // It is not possible to select some to current day.
  initialState = {
      [_today]: {disabled: true}
  }
  
  constructor() {
    super();

    this.state = {
      _markedDates: this.initialState
    }
  }
  
  onDaySelect = (day) => {
      const _selectedDay = moment(day.dateString).format(_format);
      
      let marked = true;
      if (this.state._markedDates[_selectedDay]) {
        // Already in marked dates, so reverse current marked state
        marked = !this.state._markedDates[_selectedDay].marked;
      }
      
      // Create a new object using object property spread since it should be immutable
      // Reading: https://davidwalsh.name/merge-objects
      const updatedMarkedDates = {...this.state._markedDates, ...{ [_selectedDay]: { marked } } }
      
      // Triggers component to render again, picking up the new state
      this.setState({ _markedDates: updatedMarkedDates });
  }
  
  render() {
    return (
      <View style={{flex: 1}}>
        <Calendar
            
            // we use moment.js to give the minimum and maximum dates.
            minDate={_today}
            maxDate={_maxDate}

            // hideArrows={true}

            onDayPress={this.onDaySelect}
            markedDates={this.state._markedDates}
        />
      </View>
    );
  }
}

export default WixCalendar

@francisrod01
Copy link
Author

Thank you @eddiegroves! It's almost that I make here.
The problem now is that the "marked" days doesn't update with style in this marked days.
Is there one way to do that? I think that markedDates is immutable.

@eddiegroves
Copy link

Not sure I follow, the example above should be re-rendering and showing the updated marked day automatically. Here's a tweaked example where the dotColor theme is set https://snack.expo.io/r1bMAvN7z

@francisrod01
Copy link
Author

francisrod01 commented Dec 30, 2017

@eddiegroves Thank you for your help from the beginning.
The above example (your second example) doesn't update the marked days.

Well.. I'll from your first example and tell you how it went.

EDIT:

It works for me!! Aleluia!! 🥇

image

Thank you for spread operator tips.
I'm still new to ReactJS and I did not remember that technique.

@francisrod01 francisrod01 changed the title How to toggle mark/unmark dates when onDayPress was called How to update markedDates from onDayPress event Dec 30, 2017
@eddiegroves
Copy link

@francisrod01 If you're new to React it will take a while for it to 'click' but keep at it and things like this will be easier! 👍

@francisrod01
Copy link
Author

@eddiegroves What we do it better using the new lifecycle's React?

@francisrod01 francisrod01 reopened this Aug 3, 2018
@eamonwhiter73
Copy link

eamonwhiter73 commented Sep 22, 2018

my version without moment, and I had to change two lines to get it to work (on and off):

const updatedMarkedDates = {...this.state._markedDates, ...{ [_selectedDay]: { 'selected': marked } } }

and

marked = !this.state._markedDates[_selectedDay].selected; //from marked = !this.state._markedDates[_selectedDay].marked

all code:

this.state = {
      _markedDates: this.initialState,
     ....
}

showCalendar = () => {
    return (
      <Calendar
        style={{
          borderWidth: 0,
          borderRadius: 4,
        }}
        theme={{
          todayTextColor: '#6de3dc',
          selectedDayBackgroundColor: '#6de3dc',
          selectedDayTextColor: '#ffffff',
        }}
        markingType={'custom'}
        markedDates={this.state._markedDates}
        // Initially visible month. Default = Date()
        // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
        minDate={new Date()}
        // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined
        // Handler which gets executed on day press. Default = undefined
        onDayPress={day => this.onDayPress(day)}
        // Handler which gets executed on day long press. Default = undefined
        onDayLongPress={day => {
          console.log('selected day', day)
        }}
        // Month format in calendar title. Formatting values: http://arshaw.com/xdate/#Formatting
        monthFormat={'MMM d, yyyy'}
        // Handler which gets executed when visible month changes in calendar. Default = undefined
        onMonthChange={month => {
          console.log('month changed', month)
        }}
        // Hide month navigation arrows. Default = false
        //hideArrows={true}
        // Replace default arrows with custom ones (direction can be 'left' or 'right')
        //renderArrow={(direction) => (<Arrow />)}
        // Do not show days of other months in month page. Default = false
        hideExtraDays={true}
        // If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out
        // day from another month that is visible in calendar page. Default = false
        //disableMonthChange={true}
        // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
        firstDay={0}
        // Hide day names. Default = false
        //hideDayNames={true}
        // Show week numbers to the left. Default = false
        //showWeekNumbers={true}
        // Handler which gets executed when press arrow icon left. It receive a callback can go back month
        onPressArrowLeft={substractMonth => substractMonth()}
        // Handler which gets executed when press arrow icon left. It receive a callback can go next month
        onPressArrowRight={addMonth => addMonth()}
      />
    )
  }

initialState = {
      [new Date()]: {disabled: true}
  }

onDayPress = (day) => {
      const _selectedDay = day.dateString;
      
      let marked = true;
      if (this.state._markedDates[_selectedDay]) {
        // Already in marked dates, so reverse current marked state
        marked = !this.state._markedDates[_selectedDay].selected;
      }
      
      // Create a new object using object property spread since it should be immutable
      // Reading: https://davidwalsh.name/merge-objects
      const updatedMarkedDates = {...this.state._markedDates, ...{ [_selectedDay]: { 'selected': marked } } }
      
      // Triggers component to render again, picking up the new state
      this.setState({ _markedDates: updatedMarkedDates });
  }

@eamonwhiter73
Copy link

see my stackoverflow answer for how to make them select and deselect: https://stackoverflow.com/questions/52448375/react-native-rerender-child-component-from-parent-component/52451333#52451333

@anhtuank7c
Copy link

anhtuank7c commented Sep 26, 2018

Does anyone get perf issue while setState?

@jeancabral
Copy link

Not sure I follow, the example above should be re-rendering and showing the updated marked day automatically. Here's a tweaked example where the dotColor theme is set https://snack.expo.io/r1bMAvN7z

Thx so much!!!

@FatemeMirzaeii
Copy link

Does anyone get perf issue while setState?

yes! how did you handle that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants