javascript - React Redux mapStateToProps 在获取新数据之前显示旧数据

标签 javascript reactjs redux react-redux

我的组件数据是根据输入的路由获取的 - /reports/:id/reports/1

/reports/ 后面的“1”由 match.params.id 检索,然后我对以下网址进行调度调用:

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

当用户输入无效 ID 时,即 /reports/a - 我想将用户重定向回显示登陆页面和错误消息的 /reports,像这样:

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

这一切都工作正常,直到用户尝试访问有效的“id”,即在错误的“id”之后的“/reports/1” - /reports/a,其中,用户立即重定向回 /reports 页面,因为 fetch 调用是异步的,并且尚未完成 /reports/1 的数据加载。

我已经定义了 isLoading 状态..但是如何防止这种情况发生?

<小时/>

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

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

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

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;

最佳答案

需要将templateId保存在全局状态(数据加载时设置)。仅当路径中的 templateId 等于全局状态中的 templateId 时,才需要显示组件中的数据。

关于javascript - React Redux mapStateToProps 在获取新数据之前显示旧数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54047545/

相关文章:

javascript - javascript 中的 ajax 请求 - asp.net mvc

javascript - 你如何使用 React Hooks 处理外部状态?

javascript - React + Redux 应用程序中长时间运行的进程位于何处?

reactjs - addEventListener 通过映射调度来 react redux

javascript - 将 javascript 变量的值附加到不带脚本的标签

javascript - 声明日期选择器时 knockout "observable is not a function"

javascript - 重力形式从下拉选择中显示消息

reactjs - 元素类型无效 : expected a string (for built-in components), 我的导入方式似乎不错

javascript - Next.js:如何将外部仅客户端 React 组件动态导入到开发的服务器端渲染应用程序中?

reactjs - react 条件样式无法正确渲染