Между диспетчерами была обнаружена мутация состояния по пути `...`.Это может привести к некорректному поведению - PullRequest
0 голосов
/ 08 апреля 2019

У меня есть компонент просмотра списка и календаря в родительском компоненте.Для компонента календаря я хочу иметь возможность отправлять поисковые фильтры на мой URL для фильтрации невыбранных местоположений.Я пытаюсь сгенерировать строку запроса на основе параметров, которые я предоставляю своей функции queryString, но когда я помещаю queryString в свой URL, я получаю следующую ошибку:

Между диспетчерами обнаружена мутация состоянияв пути locations.calendarLocationList.0.Это может вызвать неправильное поведение.(http://redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments)

Я не уверен, что является причиной этого, так как я не затрагивал состояние во время этого процесса.

Родительский компонент, список рендеринга ипредставление календаря

class LocationShell extends Component<
  LocationShellProps & WithNamespaces & RouteComponentProps,
  LocationShellState
  > {
  constructor(props: LocationShellProps & WithNamespaces & RouteComponentProps) {
    super(props);

    this.state = {
      isCalendarView: false,
      open: false,
      locationIdToDelete: -1,
      activePage: 1,
      activeSortHeader: undefined,
      direction: 'ascending',
      searchValue: undefined
    };
  }

  componentDidMount = (
    { loadLocations, loadSessionLocations, loadCalendarListLocations } = this.props,
    { activePage } = this.state
  ) => {
    loadLocations({ page: activePage });
    loadSessionLocations();
    loadCalendarListLocations();
  };

  toggleView = () => {
    const { isCalendarView } = this.state;
    this.setState((prevState) => ({
      ...prevState,
      isCalendarView: !isCalendarView
    }))
  }

  renderListView = () => {
    const { locationStatus, locations, selectedLocationId, history, match, pages, t } = this.props;
    const { activePage, activeSortHeader, direction } = this.state;

    switch (locationStatus) {
      case ProgressStatus.InProgress:
        return <InitialLoader />
      case ProgressStatus.Done:
        return (
          <DataTableWrapper
            // props
          />
        )
      case ProgressStatus.Error:
        return <NoDataFound />
      case ProgressStatus.Uninitialized:
        return null
    }
  }

  renderCalendarView = ({ calendarListStatus, sessionLocations, calendarListLocations } = this.props) => {
    switch (calendarListStatus) {
      case ProgressStatus.InProgress:
        return <InitialLoader />
      case ProgressStatus.Done:
        const events = toLocationEvents(sessionLocations!);
        return <CalendarView {...this.props} events={events} items={calendarListLocations!} name={'locations'} />
      case ProgressStatus.Error:
        return <NoDataFound />
      case ProgressStatus.Uninitialized:
        return null
    }
  }

  render() {
    const { pathName, t } = this.props;
    const { isCalendarView } = this.state;
    return (
      <Fragment>
        <PageHeader
          breadCrumbParts={split(pathName, '/').map(x => t(x))}
          title={t('moduleTitle')}
        />
        <Button.Group size="mini" style={{ padding: '10px 5px 10px 0px' }}>
          <Button positive={!isCalendarView} onClick={this.toggleView}>Lijst</Button>
          <Button.Or />
          <Button positive={isCalendarView} onClick={this.toggleView}>Kalender</Button>
        </Button.Group>
        <Button
          positive
          icon='add'
          size="mini"
          labelPosition='right'
          content="Nieuwe locatie"
          onClick={() => this.props.history.push(this.props.match.path + '/create')}
        />
        {isCalendarView ? this.renderCalendarView() : this.renderListView()}
      </Fragment>
    );
  }
}

const mapStateToProps = (state: GlobalState) => {
  return {
    locations: getLocations(state.locations),
    calendarListLocations: state.locations.calendarLocationList,
    calendarListStatus: state.locations.calendarListStatus,
    sessionLocations: state.locations.sessionLocations,
    selectedLocation: getSelectedLocation(state.locations),
    selectedLocationId: getSelectedLocationId(state.locations),
    pages: getAmountPages(state.locations),
    locationStatus: state.locations.locationStatus,
    sessionLocationStatus: state.locations.sessionLocationStatus,
    pathName: getPathName(state.router)
  };
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
  loadLocations: (queryParams: QueryParams) =>
    dispatch(FetchLocations(queryParams)),
  loadSessionLocations: () => dispatch(FetchTrainingSessionLocations({})),
  loadCalendarListLocations : () => dispatch(FetchCalendarListLocations({})),
  clearLocations: () => dispatch(ClearLocations()),
  deleteLocation: (id: number) => dispatch(DeleteLocation({ locationId: id }))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withNamespaces('locations')(LocationShell));

renderCalendarView() отображает компонент моего календаря

Мой компонент календаря:

interface CalendarViewState {
    selectedIds: number[];
}

type CalendarViewProps = {
    events: CalendarEvent[];
    name: string;
    items: CalendarListLocation[];
    navigatePush: (values: string) => void;
} & RouteComponentProps

class CalendarView extends Component<CalendarViewProps & WithNamespaces, CalendarViewState> {

    state: CalendarViewState = {
        selectedIds: []
    }

    componentDidMount = () => {
        const { events, items } = this.props;
        const { baseUrl, newEntity } = moduleConstants;
        this.setState((prevState) => ({
            ...prevState,
            selectedIds: items.map(x => x._id)
        }), () => {
            updateQueryString(this.props, { page: 1, locations: [1, 2] })
        }
        )
    }

    queryParams(props: CalendarViewProps = this.props) {
        return queryParams<QueryParams>(props.location.search);
    }
    componentDidUpdate = (prevProps: CalendarViewProps, prevState: CalendarViewState) => {
        const { selectedIds } = this.state;
        console.log()
        if (!isEqual(prevState.selectedIds, selectedIds)) {
            console.log(this.queryParams())
        }
    }
    handleChange = (id: number) => {
        const { selectedIds } = this.state;
        this.setState((prevState) => ({
            ...prevState,
            selectedIds: (selectedIds.includes(id) ? selectedIds.filter(x => x !== id) : [...selectedIds, id])
        }));
    };

    render() {
        const { events, name, t, items } = this.props
        return (
            <Grid divided="vertically" padded>
                <Grid.Row columns={2}>
                    <Grid.Column width={4}>
                        <CalendarSelectionList
                            name={t(name)}
                            onSelectionChange={this.handleChange}
                            selectedIds={this.state.selectedIds}
                            locations={items.sort((a: CalendarListLocation, b: CalendarListLocation) => a.name.localeCompare(b.name))}
                        />
                    </Grid.Column>
                    <Grid.Column width={12}>
                        <div style={{ height: '800px' }}>
                            <Calendar
                                events={events.filter(x => this.state.selectedIds.includes(x.id))}
                            />
                        </div>
                    </Grid.Column>
                </Grid.Row>
            </Grid>
        );
    }
}

const mapDispatchToProps = (dispatch: Dispatch) => ({
    navigatePush: (path: string) => dispatch(push(path))
});

export default connect(
    null,
    mapDispatchToProps
)(withNamespaces(['calendar'])(CalendarView));

updateQueryString(this.props, { page: 1, locations: [1, 2] })Когда эта функция запускается, эта функция обновляет URL-адрес сгенерированным queryString

export function queryParams<T>(search: string) {
  return (queryString.parse(search) as unknown) as T;
}

export function updateQueryString<T>(props: RouteComponentProps, newValues: T) {
  const currentQuery = queryParams<T>(props.location.search);
  const newProps = Object.assign(currentQuery, newValues);
  props.history.push({
    pathname: props.location.pathname,
    search: queryString.stringify(filterSearchResults(newProps))
  });
}
function filterSearchResults(values: any) {
  let obj: any = {};
  Object.keys(values).forEach(
    key => values[key] && (obj[key] = values[key])
  );
  return obj;
}

. После этого возникает вышеуказанная ошибка. Почему происходит эта ошибка?

1 Ответ

0 голосов
/ 08 апреля 2019

Ошибка означает, что locations.calendarLocationList был видоизменен, в то время как хранилище Redux должно быть неизменным.

calendarLocationList используется как items в CalendarView и изменяется с items.sort(...), поскольку массив sort изменяет существующий массив вместо создания нового.

Это можно исправить с помощью [...items].sort(...).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...