简体   繁体   English

在获取新数据之前反应Redux mapStateToProps显示旧数据

[英]React Redux mapStateToProps showing old data before new data fetch

My component data is fetched based on the route entered - /reports/:id ie /reports/1 根据输入的路径获取我的组件数据- /reports/:id/reports/1

the "1" following the /reports/ is retrieved by match.params.id which I then make a dispatch call to the following url: /reports/之后的“ 1”由match.params.id检索,然后我对以下网址进行调度调用:

fetchDashData(`http://ee-etap.devops.fds.com/api/etap/v1/templates/template/report/${match.params.id}`)

When the user enter an invalid id, ie /reports/a - I want to redirect the user back to /reports which displays a landing page and error message, as such: 当用户输入无效的ID(即/reports/a -我想将用户重定向回/reports ,该页面显示着陆页和错误消息,例如:

  return <Redirect to={{
      pathname: '/reports',
      state: { templateId: match.params.id } }}
    />;

This all works fine until when the user try to visit a valid 'id', ie /reports/1 right after the erroneous one - /reports/a , in which the user is immediately redirected back to the /reports page because the fetch call is asynchronous and haven't finished loading the data for /reports/1 . 一切正常,直到用户尝试访问有效的“ id”,即错误的一个- /reports/a之后的/reports/1为止,在该用户中,由于提取调用,用户立即被重定向回/reports页面是异步的,尚未完成/reports/1的数据加载。

I already have isLoading state defined.. but how can I prevent this from happening? 我已经定义了isLoading状态。但是如何防止这种情况发生?


ReportsDashboard.jsx ( /reports/:id ) ReportsDashboard.jsx/reports/:id

 class ChartsDashboard extends React.Component {
  componentDidMount() {
    const { fetchDashData, data, isLoading, hasErrored, match } = this.props;

    if ( match.params && match.params.id ) {
      fetchDashData(`http://ee-etap.devops.fds.com/api/etap/v1/templates/template/report/${match.params.id}`);
    }
  }
   render() {
    const { data, hasErrored, isLoading, classes, match } = this.props;    

    if ( isLoading ) {
      return (
        <div style={{ margin: '0 auto', textAlign: 'center' }}>
          <CircularProgress size={50} color="secondary" />
        </div>
      );
    }

    if ( data && data !== null ) {
      const { TemplateReport } = data;
      const {
        errorBarChart, historyTriggers, historyLineChart, jobs, lastBuildDonutChart, features,
      } = TemplateReport;

      if (errorBarChart.length === 0) {
        // error in data
        return <Redirect to={{
          pathname: '/reports',
          state: { templateId: match.params.id } }}
        />;
      }

      const keys = [];
      errorBarChart.forEach((errorItem) => {
        Object.keys(errorItem).forEach((errorKey) => {
          if (errorKey !== 'category') {
            keys.push(errorKey);
          }
        });
      });

      if (match.params.id) {
        return (
          <div className="page-container">
            <Grid container spacing={24}>
              <Grid item xs={12} lg={4}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Error By Categories</h4>
                  <div style={{ height: '350px' }}>
                    <ResponsiveBar
                      data={errorBarChart}
                      keys={keys}
                      indexBy="category"
                      margin={{
                        top: 50,
                        right: 50,
                        bottom: 50,
                        left: 50,
                      }}
                      padding={0.1}
                      colors="paired"
                      colorBy="id"
                      axisBottom={{
                        orient: 'bottom',
                        tickSize: 5,
                        tickPadding: 5,
                        tickRotation: 0,
                        legend: 'CATEGORY',
                        legendPosition: 'middle',
                        legendOffset: 36,
                      }}
                      axisLeft={{
                        orient: 'left',
                        tickSize: 5,
                        tickPadding: 5,
                        tickRotation: 0,
                        legend: 'ERROR COUNT',
                        legendPosition: 'middle',
                        legendOffset: -40,
                      }}
                      labelSkipWidth={12}
                      labelSkipHeight={12}
                      labelTextColor="inherit:darker(1.6)"
                      animate
                      motionStiffness={90}
                      motionDamping={15}
                    />
                  </div>
                </Paper>
              </Grid>
              <Grid item xs={12} lg={4}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Pass Rate %</h4>
                  <div style={{ height: '350px' }}>
                    <ResponsivePie
                      colors="paired"
                      colorBy={this.pieColors}
                      margin={{
                        top: 40,
                        right: 40,
                        bottom: 40,
                        left: 40,
                      }}
                      data={lastBuildDonutChart}
                      animate
                      defs={[
                        linearGradientDef('gradientRed', [{ offset: 0, color: 'red' }, { offset: 100, color: '#ffcdd2', opacity: 0.3 }]),
                        linearGradientDef('gradientYellow', [{ offset: 0, color: 'yellow' }, { offset: 100, color: '#f7bf18a3', opacity: 0.3 }]),
                        linearGradientDef('gradientGreen', [{ offset: 0, color: '#38da3e' }, { offset: 100, color: '#38da3e', opacity: 0.3 }]),
                      ]}
                      fill={[
                        { match: { id: 'Fail' }, id: 'gradientRed' },
                        { match: { id: 'Pass' }, id: 'gradientGreen' },
                        { match: { id: 'Undefined' }, id: 'gradientYellow' },
                      ]}
                      radialLabelsSkipAngle={10}
                      radialLabelsTextXOffset={6}
                      radialLabelsTextColor="#333333"
                      radialLabelsLinkOffset={0}
                      radialLabelsLinkDiagonalLength={8}
                      radialLabelsLinkHorizontalLength={7}
                      radialLabelsLinkStrokeWidth={1}
                      radialLabelsLinkColor="inherit"
                      innerRadius={0.5}
                      padAngle={0.7}
                      cornerRadius={3}
                    />
                  </div>
                </Paper>
              </Grid>

              <Grid item xs={12} lg={4}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Jobs Triggered</h4>
                  <JobsTable data={jobs} templateId={match.params.id} />
                </Paper>
              </Grid>

              <Grid item xs={12} lg={12}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Scenarios Table</h4>
                  <Tooltip title="Scenario Report">
                    <a href={`/reports/${match.params.id}/scenarioHistory`} rel="noopener noreferrer">
                      <IconButton aria-label="Scenario Report">
                        <AssignmentIcon />
                      </IconButton>
                    </a>
                  </Tooltip>
                  <ScenariosTable data={features} />
                </Paper>
              </Grid>

              <Grid item xs={12} lg={12}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Execution History</h4>
                  <div style={{ height: '400px' }}>
                    <ResponsiveLine
                      colors="paired"
                      colorBy="id"
                      margin={{
                        top: 20,
                        right: 20,
                        bottom: 60,
                        left: 80,
                      }}
                      data={historyLineChart}
                      enableArea={true}
                      animate
                      yScale={{ type: 'linear', stacked: true }}
                    />
                  </div>
                </Paper>
              </Grid>

              <Grid item xs={12}>
                <Paper className={classes.paper}>
                  <h4 className={classes.heading}>Previous Builds</h4>
                  <PreviousBuildsTable data={historyTriggers} templateId={match.params.id}/>
                </Paper>
              </Grid>
            </Grid>
          </div>
        );
      }
    }    

    // error in data
    return <Redirect to={{
                pathname: '/reports',
                state: { templateId: match.params.id } }}
    />;

  }
}

const mapStateToProps = state => ({
  data: state.reports.data,
  hasErrored: state.reports.hasErrored,
  isLoading: state.reports.isLoading,
});

const mapDispatchToProps = dispatch => ({
  fetchDashData: url => dispatch(chartDataFetch(url)),
});

export default compose(
  withStyles(styles),
  withRouter,
  connect(
    mapStateToProps,
    mapDispatchToProps,
  ),
)(ChartsDashboard);

BrowseReport.jsx ( /reports/ ) BrowseReport.jsx/reports/

class BrowseReports extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      searchVal: '',
      errorMsg: '',
    }

    this.onSearchChange = this.onSearchChange.bind(this);
    this.goToTemplateReport = this.goToTemplateReport.bind(this);
  }

  componentDidMount() {
    if (this.props.location && this.props.location.state && this.props.location.state.templateId) {
      this.state.errorMsg = `Template Name "${this.props.location.state.templateId}" does not exist, please try again`;
      this.props.history.replace('/reports', null);
    }
  }

  onSearchChange(val) {
    this.setState({ searchVal: val });
  }

  goToTemplateReport(val) {
    this.props.history.push(`/reports/${val}`);
  }

  render() {
    const { classes, location } = this.props;
    const { searchVal, errorMsg } = this.state;

    return (
      <div className="page-container" style={{ textAlign: 'center' }}>
        <Grid container justify="center" spacing={24}>
          <Grid item xs={12} lg={8}>

          {/* dashData Error */}

            <h4 className={classes.errorMsg}>
              {/* ERROR MESSAGE HERE  */}
              {errorMsg}
            </h4>
            <SearchBar
              value={this.state.searchVal}
              placeholder='Search for Template Name'
              onChange={(value) => this.onSearchChange(value)}
              onRequestSearch={(value) => this.goToTemplateReport(value)}
              style={{
                margin: '0 auto',
              }}
            />
          </Grid>
          <Grid item xs={12} lg={6}>
              <Paper className={classes.paper}>
                <CompletedJobsTable></CompletedJobsTable>
              </Paper>
          </Grid>
          <Grid item xs={12} lg={6}>
              <Paper className={classes.paper}>
                <ActiveJobsTable></ActiveJobsTable>
              </Paper>
          </Grid>
        </Grid>
      </div>
    )
  }
}

export default compose(
  withStyles(styles),
  withRouter
)(BrowseReports);

actions.jsx actions.jsx

export const chartDataHasErrored = hasErrored => ({
  type: CHARTS_DATA_HAS_ERRORED,
  payload: { hasErrored },
});

export const chartDataIsLoading = isLoading => ({
  type: CHARTS_DATA_IS_LOADING,
  payload: { isLoading },
});

export const chartDataFetchSuccess = data => ({
  type: CHARTS_DATA_FETCH_SUCCESS,
  payload: { data },
});

export const chartDataFetch = url => (dispatch) => {
  dispatch(chartDataIsLoading(true));
  fetch(url, { mode: 'cors' })
    .then((response) => {
      if (!response.ok) {
        throw Error(response.statusText);
      }
      return response;
    })
    .then(response => response.json())
    .then((items) => {
      dispatch(chartDataFetchSuccess(items));
    })
    .catch((error) => {
      dispatch(chartDataHasErrored(error));
    });
};

reducers.jsx reducers.jsx

import { CHARTS_DATA_FETCH_SUCCESS, CHARTS_DATA_IS_LOADING, CHARTS_DATA_HAS_ERRORED } from '../../../store/actions';

const INITIAL_STATE = {
  hasErrored: null,
  isLoading: true,
  data: {},
}

const reportsDashboardReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {

    case CHARTS_DATA_HAS_ERRORED:
      return {
        ...state,
        hasErrored: action.payload.hasErrored,
        isLoading: false,
      };

    case CHARTS_DATA_IS_LOADING:
      return {
        ...state,
        isLoading: action.payload.isLoading,
        hasErrored: null,
      };

    case CHARTS_DATA_FETCH_SUCCESS:
      return {
        ...state,
        isLoading: false,
        data: action.payload.data,
      };    

    default:
      return state;
  }
};

export default reportsDashboardReducer;

You need to save templateId in the global state (set it when data loaded). 您需要将templateId保存为全局状态(在加载数据时进行设置)。 In component data need to be shown only if templateId from the path equal to the templateId from the global state. 仅当路径中的templateId等于全局状态中的templateId时,才需要显示组件数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM