React-Redux-对象道具更新不会重新渲染子组件

时间:2020-07-14 12:41:58

标签: javascript reactjs redux react-redux

我正在使用react和redux开发日历。

我有一个父组件(App),该组件传递给称为事件的子组件(MonthlyCalendar)道具。

events是一个对象。 事件中的每个值都是一个数组。 任何事件都将从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

i在componentDidUpdate上将forceUpdate添加到了父组件,但仍然没有重新呈现子组件(MonthlyCalendar):

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

编辑2

可能是与路线有关的问题。我读了有关路线https://learnwithparam.com/blog/how-to-pass-props-in-react-router/#:~:text=Passing%20function%20as%20a%20component%20props%20in%20Route%20component&text=Internally%2C%20react%20router%20use%20React,just%20updating%20the%20existing%20component.

中的渲染组件的文章

添加了带有props和{... props}的渲染器仍然无法执行:

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

编辑3

看来,如果我走这条路线,一切正常,孩子会重新渲染。有人对此行为有解释吗?

0 个答案:

没有答案