Search code examples
javascriptreact-nativecomponentsrenderingparent-child

React Native - rerender child component from parent component


I am trying to modify a calendar - https://github.com/wix/react-native-calendars so that when I press a day, the background and text colors change for that day. I read that to re-render a component you need to update the state of the parent component, so that is what I am trying:

Parent.js

export default class InitiateRent extends Component {
  state = {
    ....
    this.markedDays: {},
    someVar: 0
  };

  constructor() {
    super(); 
    this.handler = this.handler.bind(this);
    this.markedDays = {};
  }

  ....

  handler(e) {
    console.log('handler running');
    this.setState({
      someVar: 123
    })
  }

  <Calendar 
    handler={this.handler}
    markedDates={this.markedDays}
    onDayPress={(day)=> {
      console.log('selected day', day);

      var dateString = `'${day.year}-${day.month}-${day.day}'`

      var addDate = {
        [dateString]: {
          customStyles: {
            container: {
              backgroundColor: '#6de3dc',
            },
            text: {
              color: 'white',
              fontWeight: 'bold'
            },
          },
        }
      }

      this.markedDays = Object.assign(addDate, this.markedDays);

      console.log(this.markedDays);
    }}

....

Child.js

pressDay(date) {
    this.props.handler();
    this._handleDayInteraction(date, this.props.onDayPress);
}

renderDay(day, id) { //GETS CALLED IN CHILD RENDER METHOD VIA AN INTERMEDIARY
    ....
        <DayComp
          ....
          onPress={this.pressDay}
          ....
}

Everything functions correctly except I don't get the expected result - when I press a date on the calendar, the handler fires, the state changes, and in the console my object looks correct:

Object

'2018-9-23': {customStyles: {container: {backgroundColor: "#6de3dc"}, text: {color: "white", fontWeight: "bold"}}}

'2018-9-26': {customStyles: {container: {backgroundColor: "#6de3dc"}, text: {color: "white", fontWeight: "bold"}}}

'2018-9-28': {customStyles: {container: {backgroundColor: "#6de3dc"}, text: {color: "white", fontWeight: "bold"}}}

'2018-9-29': {customStyles: {container: {backgroundColor: "#6de3dc"}, text: {color: "white", fontWeight: "bold"}}}

But the background of the day on the calendar doesn't change, neither does the text - what do I have to do to get it to re-render (change)?

UPDATE

I created a chain of handlers all the way from the very bottom day component which represents just a day on the calendar all the way up to the highest order parent view (not App.js, but just below it) and that still did nothing.

UPDATE

The calendar is on a modal, and I don't have any handler on that updating its state, could that be the problem?

UPDATE

I found this in the docs:

!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.

What does this mean?

UPDATE

I tried to interpret what that meant with some google searches, does this make this.state.markedDays immutable?:

                const markedDays = this.state.markedDays;

                var dateString = `'${day.year}-${day.month}-${day.day}'`;

                var addDate = {
                  [dateString]: {
                    customStyles: {
                      container: {
                        backgroundColor: '#6de3dc',
                      },
                      text: {
                        color: 'white',
                        fontWeight: 'bold'
                      },
                    },
                  }
                }

                const markedDaysHolder = {
                  ...markedDays,
                  ...addDate
                }
                
                this.state.markedDays = markedDaysHolder;

UPDATE

I put:

componentDidUpdate(prevProps, prevState, snapshot) {
  console.log("componentDidUpdate in InitiateRent:", prevProps, prevState, snapshot);
}

The output of the above is:

componentDidUpdate in InitiateRent: (3) (index.bundle, line 761)

{initiateRentMessage: function, modalHeight: 200, modalWidth: 200, handler: function}

{modalVisible: true, message: "Hi, I would like to rent an item from you.", rentButtonBackground: "#6de3dc", someVar: 123, markedDays: Object}

undefined

I can see the state object markedDays getting bigger with each button press from this output, why isn't the style changing?

In all the relevant components, and I noticed it doesn't fire on the lowest order component, which is the one that needs to change.


Solution

  • I also had this problem, this is what you need to do

    constructor(props) {
        super(props)
        this.state = {
        }
        this.onDayPress = this.onDayPress
      }
    
    showCalendar = () => {
        return (
          <Calendar
            onDayPress={this.onDayPress}
            style={styles.calendar}
            hideExtraDays
            markedDates={{
              [this.state.selected]: {
                selected: true,
                disableTouchEvent: true,
                selectedDotColor: 'orange',
              },
            }}
          />
        )
      }
    
    onDayPress = day => {
        this.setState({
          selected: day.dateString,
        })
      }