React-Redux - обновление свойств объекта не повторно отображает дочерний компонент - PullRequest
1 голос
/ 14 июля 2020

Я разрабатываю календарь, используя реакцию и сокращение.

У меня есть родительский компонент (приложение), который передает свойства дочернего компонента (MonthlyCalendar), называемые событиями.

события - это объект. Каждое значение в событиях представляет собой массив. Любые события обновляются из redux, повторно визуализируются родительский компонент, но не дочерний компонент, который использует события.

Первый жизненный цикл дочернего компонента - единственный, который выполняется, после этого нет обновления жизни- цикл. Несмотря на то, что реквизит изменился с пустого объекта на объект со свойствами. Я видел, что это известная проблема - реквизиты объекта, но ни одно из решений не помогло. Если это дубликат, извините, мне кажется, я пробовал их все.

Я пробовал деструктурировать события в дочернем и родительском элементах.

Я пытался использовать JSON .parse и stringfy.

Добавление ключа на основе длины реквизитов событий в дочернем элементе.

Я удалил все setState и действия, которые могут манипулировать состоянием, но родительский рендеринг по-прежнему ребенок нет. Проверка выполняется console.log на каждом жизненном цикле дочернего компонента, который печатается только один раз при монтировании компонента. А при отладке показано, что обновленные события достигают функции рендеринга родительского.

Родительский компонент - App.tsx

class App extends React.Component<AppProps> {
  state = {
    events: {},
    weekStarter: 'sunday',
    isTimeShown: true,
    isTimeZoneShown: true,
    isTodayButtonStyleSeconday: true,
    locale: 'en',
    timeZone: '',
  };

  constructor(props: any) {
    super(props);

    this.fetchEvents = this.fetchEvents.bind(this);
  }
  componentDidMount() {
    window.Wix.Styles.getStyleParams((styleParams: any) => {
      this.updateStyle(styleParams);
      console.log('getStyleParams', styleParams);
    });
    window.Wix.addEventListener(
      window.Wix.Events.STYLE_PARAMS_CHANGE,
      (styleParam: Object) => {
        console.log('STYLE_PARAMS_CHANGE', styleParam);
        this.updateStyle(styleParam);
      },
    );
  }

...

render() {
    const { t, events } = this.props;
    return (
      <Switch>
        <Route
          path="/index"
          render={() => (
            <MonthlyCalendar
              weekStarter={this.state.weekStarter}
              events={{...events}}
              handleMonthChange={handleMonthChange}
              isTimeZoneShown={this.state.isTimeZoneShown}
              isTimeShown={this.state.isTimeShown}
              locale={this.state.locale}
              //timeZone={this.state.timeZone}
              isTodayButtonStyleSeconday={this.state.isTodayButtonStyleSeconday}
            />
          )}
        ></Route>
        <Route
          path="/settings"
          render={() => (
            <Settings
              fetchEvents={this.fetchEvents}
              initialState={this.props.initialState}
              weekStarter={this.state.weekStarter}
            />
          )}
        ></Route>
        <Route
          path="/mobile"
          render={() => (
            <MonthlyCalendar
              weekStarter={this.state.weekStarter}
              events={events}
              handleMonthChange={handleMonthChange}
              isTimeZoneShown={this.state.isTimeZoneShown}
              isTimeShown={this.state.isTimeShown}
              locale={this.state.locale}
              isTodayButtonStyleSeconday={this.state.isTodayButtonStyleSeconday}
            />
          )}
        ></Route>
      </Switch>
    );
  }
}
const mapDispatchToProps = (dispatch: any) => ({});
const mapStateToProps = (state: any) => ({
  isConnect: state.settings.isConnect,
  userName: state.settings.userName,
  events: state.settings.dates,
  calendarsList: state.settings.calendarsList,
  googleSettings: state.settings.googleSettings,
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

Ребенок - MonthlyCalendar.tsx

class MonthlyCalendar extends React.Component<
  IMonthylCalendarProps & WithTranslation,
  IMonthylCalendarState
  > {
  monthlyCalendarRef: React.RefObject<HTMLDivElement>;

  public static defaultProps = {
    locale: 'en',
    weekStarter: WeekStartersEnum.Sunday,
    events: [],
    isTimezonesOpen: true,
    isTodayButtonStyleSeconday: true,
    isTimeZoneShown: true,
    isTimeShown: true
  };

  constructor(props: any) {
    super(props);
    console.log('[constructor] props.events: ',props.events)
    const timezone = moment.tz.guess();
    const dateObject = moment().tz(timezone, true);
    this.state = {
      dateObject,
      timezone,
      isTimezonesOpen: false,
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log('[shouldComponentUpdate] props.events: ',this.props.events)
    return true
  }

...

getCalendar() {
    const mutableEvents = {...this.props.events};
    const { dateObject } = this.state;
    const beforeFillers = this.getMonthBeforFillers(dateObject, mutableEvents);
    const days = this.getDays(dateObject, mutableEvents);
    const afterFillers = this.hasAfterFillers(beforeFillers, days) ?
      this.getAfterMonthFillers(dateObject, mutableEvents) : {};
    return { days, beforeFillers, afterFillers };
  }

  async componentDidUpdate(prevProps) {
    console.log('[componentDidUpdate] props.events: ',this.props.events)
    this.props.locale !== prevProps.locale && await this.updateLocale();
  }

  async componentDidMount() {
    console.log('[componentDidMount] props.events: ',this.props.events)
    this.props.locale !== 'en' && await this.updateLocale();
  }

  render() {
    const { t, weekStarter, isTodayButtonStyleSeconday, isTimeZoneShown, isTimeShown, events: propEvents } = this.props;
    const eventsKey = Object.keys(propEvents).length;
    const { dateObject, timezone, isTimezonesOpen } = this.state;
    const { days, beforeFillers, afterFillers } = this.getCalendar();
    const month = dateObject.format(t('Google_Calendar_Picker_Month'));
    const timezoneSelected = moment().tz(timezone).format(t('Google_Calendar_Timezone_Selected'));
    const timezoneSelectedTitle = t('Google_Calendar_Timezone_Selected_Title', { timezoneSelected });
    console.log('[render] props.events: ',this.props.events)
    return (
      <TPAComponentsProvider value={{ mobile: false, rtl: false }}>
        <div key={eventsKey} className={classes.MonthlyCalendar}>
          <CalendarControllers
            isTodayButtonStyleSeconday={isTodayButtonStyleSeconday}
            todayClicked={this.todayClickedHander}
            onPreviousClicked={() => this.timePickerClickedHandler(false)}
            timeToDisplay={month}
            onNextClicked={() => this.timePickerClickedHandler(true)}
            onTimezoneChange={this.timezoneChangeHandler}
            timezone={timezoneSelectedTitle}
            isTimezonesOpen={isTimezonesOpen}
            openTimezones={this.openTimezones}
            closeTimezones={this.closeTimezones}
            isTimeZoneShown={isTimeZoneShown}
          />
          <MonthTable
            weekStarter={weekStarter}
            days={days}
            beforeFillers={beforeFillers}
            dateObject={dateObject}
            afterFillers={afterFillers}
            renderCell={(
              time: any,
              events: any,
              cellRef: any,
              handleEventClick: any,
              setExpendedEvent: any,
              expendedEvent: any,
              isOutsideClicked: any,
            ) => (
                <MonthlyCell
                  events={events}
                  handleEventClick={handleEventClick}
                  time={time}
                  cellRef={cellRef}
                  expendedEvent={expendedEvent}
                  isOutsideClicked={isOutsideClicked}
                  setExpendedEvent={setExpendedEvent}
                  isTimeShown={isTimeShown}
                />
              )}
          />
        </div>
      </TPAComponentsProvider>
    );
  }
}

export default withTranslation()(MonthlyCalendar);

любая помощь приветствуется

РЕДАКТИРОВАТЬ 1

Я добавил forceUpdate в родительский компонент в componentDidUpdate, по-прежнему нет повторной визуализации дочернего компонента (MonthlyCalendar):

componentDidUpdate(prevProps: AppProps) {
    const prevEvents = JSON.stringify(prevProps.events);
    const currentEvents = JSON.stringify(this.props.events);
    if ( prevEvents !== currentEvents) {
      this.forceUpdate()
    }
  }

EDIT 2

возможно, проблема связана с маршрутом. Я читал статью о рендеринге компонента в маршруте: https://learnwithparam.com/blog/how-to-pass-props-in-react-router/#: ~: text = Передача% 20function% 20as% 20a% 20component% 20props% 20in% 20Route% 20component & text = Внутренне% 2C% 20react% 20router% 20use% 20React, просто% 20updating% 20the% 20existing% 20component.

добавлен рендеринг с props и {... props} все еще нет go:

<Switch>
        <Route
          path="/index"
          render={(props: any) => (
            <MonthlyCalendar
            {...props}
              weekStarter={this.state.weekStarter}
              events={{...this.state.events}}
              handleMonthChange={handleMonthChange}
              isTimeZoneShown={this.state.isTimeZoneShown}
              isTimeShown={this.state.isTimeShown}
              locale={this.state.locale}
              //timeZone={this.state.timeZone}
              isTodayButtonStyleSeconday={this.state.isTodayButtonStyleSeconday}
            />
          )}
        ></Route>

EDIT 3

кажется, что если я выберу маршрут, все будет работать, и дочерний элемент будет перерисован. у кого-нибудь есть объяснение этому поведению?

...