0

对于我一直在工作的仪表板组件,最近遇到了一些问题。我正在使用 react-router 来处理客户端的 url 路由,最近我开始为每个路由异步加载组件。

每当我从路由组件发出 http 请求时,我都会在控制台中收到一条警告消息。警告说 setState 不能是未安装组件的状态。到目前为止,我已经能够推断出安装循环可能导致 setState 无法成功设置组件的状态。除此之外,我不确定是什么导致此问题出现。

以前有没有其他人有这个问题?对此问题的任何建议表示赞赏。

控制台截图在此处输入图像描述

asyncLoader.js

 import React from 'react';

 export default (getComponent, extraProps=null) => {
     return class asyncComponent extends React.Component {
         static Component = null;

         constructor(props){
             super(props);

             this.state = {Component: asyncComponent.Component};
         }
         componentWillMount(){
             const {Component} = this.state;

             if (Component === null) getComponent().then((component) => {
                asyncComponent.Component = component;

                if (this.mounted) this.setState({Component: component});

             });
         }
         componentDidMount(){
             this.mounted = true;
         }
         componentWillUnmount(){
             this.mounted = false;
         }
         render(){
             const {Component} = this.state;

             return Component !== null ? <Component {...this.props} {...extraProps} /> : null;

         }
     };
};

路由容器.js

import React from 'react';
import {Switch, Route, Redirect} from 'react-router-dom';
import asyncComponent from './asyncLoader';

export default class Container extends React.Component{
    constructor(props){
        super(props);

        this.state = {loadingScreen: true};

        this.handleStateChange = this.handleStateChange.bind(this);
    }
    handleStateChange(){
        this.setState((prevState) => ({loadingScreen: !prevState.loadingScreen}));
    }
    render(){
        const {user, getUserCallback} = this.props,
              {loadingScreen} = this.state,
              UserSettings = asyncComponent(() => import('./user').then((module) => module.UserSettings), {user: {...user}, getUserCallback: getUserCallback, loadingScreenCallback: this.handleStateChange}),
              TransactionHistory = asyncComponent(() => import('./transactions').then((module) => module.TransactionHistory), {user: {...user}, loadingScreenCallback: this.handleStateChange, axiosInstance: this.props.axiosInstance}),
              PaymentMethods = asyncComponent(() => import('./billing').then((module) => module.PaymentMethods), {loadingScreenCallback: this.handleStateChange, axiosInstance: this.props.axiosInstance}),
              UserBillCreator = asyncComponent(() => import('./billing').then((module) => module.UserBillCreator), {loadingScreenCallback: this.handleStateChange, axiosInstance: this.props.axiosInstance}),
              UserBillPaymentSender = asyncComponent(() => import('./billing').then((module) => module.UserBillPaymentSender), {loadingScreenCallback: this.handleStateChange, axiosInstance: this.props.axiosInstance, getUserCallback: getUserCallback, accountBalance: !user.admin ? user.account_balance : 0});

        return(
            <div className="d-flex flex-column col dashboard-app-container">
                <div ref="loadingScreen" className={loadingScreen ? "row justify-content-center react-loading-screen" : "row justify-content-center react-loading-screen hide"}>
                    <div className="d-flex flex-column justify-content-center react-loading-container">
                        <i className="fa fa-spin fa-circle-o-notch" />
                    </div>
                </div>
                <Switch>
                   <Route path="/settings" component={UserSettings} />
                   <Route path="/transactions/history" component={TransactionHistory} />
                   <Route path="/transactions/paymentMethods" component={PaymentMethods} />
                   <Route path="/transactions/billing/create" component={UserBillCreator} />
                   <Route path="/transactions/billing/pay" component={UserBillPaymentSender} />
                   <Redirect from="/" to="/transactions/history" />
                </Switch>
            </div>
        );
    }

}

路由“/transactions/history”的组件示例

交易.js

import React from 'react';
import _ from 'lodash';
import moment from 'moment';
import {Link} from 'react-router-dom';
import {AgGridReact} from 'ag-grid-react';
import DayPickerInput from 'react-day-picker/DayPickerInput';
import 'react-day-picker/lib/style.css';


export class TransactionHistory extends React.Component{
    constructor(props){
        super(props);

        this.state = {
            activity_day_period: '30 days',
            date_filter: {
                start: '',
                end: ''
            },
            dateAccordianOpen: false,
            data_grid: {
                columns: [
                    {headerName: 'Bill ID', field: 'id'},
                    {headerName: 'Created On', field: 'created_on'},
                    {headerName: 'Paid on', field: 'transaction_created_on'},
                    {headerName: 'Amount', field: 'billed_amount'},
                    {headerName: 'Item', field: 'item_name'},
                    {headerName: 'Description', field: 'description'}

                ],
                rows: [],
                filtered_rows: [],
                raw_data: []
            }
        };

        this.handleDateChangeStart = this.handleDateChangeStart.bind(this);
        this.handleDateChangeEnd = this.handleDateChangeEnd.bind(this);
        this.onGridReady = this.onGridReady.bind(this);
        this.handGridResize = this.handleGridResize.bind(this);
        this.getTransactionData = this.getTransactionData.bind(this);

    }
    componentDidMount(){
        this.getTransactionData();
        setTimeout(this.props.loadingScreenCallback, 600);
    }
    componentWillUnmount(){
        this.props.loadingScreenCallback();
    }
    handleDateChangeStart(date){
        this.setState((prevState) => ({date_filter: {...prevState.date_filter, start: moment.utc(date).format('YYYY-MM-DD')}}));

    }
    handleDateChangeEnd(date){
        this.setState((prevState) => ({date_filter: {...prevState.date_filter, end: moment.utc(date).format('YYYY-MM-DD')}}));
    }
    onGridReady(params){
        this.api = params.api;
        this.columnApi = params.columnApi;

        this.api.sizeColumnsToFit();

    }
    handleGridResize(){
        this.api.sizeColumnsToFit();

    }
    getTransactionData(){
        const {activity_day_period} = this.state;
        let api_url = window.location.origin;

        switch (activity_day_period) {
            case '30 days':
                api_url = `${api_url}/api/user/billing?days=30`;
                break;

            case '3 months':
                api_url = `${api_url}/api/user/billing?months=3`;
                break;

            case '6 months':
                api_url = `${api_url}/api/user/billing?months=6`;
                break;

            case '1 year':
                api_url = `${api_url}/api/user/billing?years=1`;
                break;

            case 'All':
                api_url = `${api_url}/api/user/billing`;
                break;

        }

        this.props.axiosInstance.get(api_url).then((response) => {
            if (response.data.code == 200) {
                // The warning seems to pop up when setState in this method is called...
                this.setState((prevState) => ({
                    data_grid: {
                        ...prevState.data_grid,
                        rows: response.data.bills.map((data) => {
                            if (data.bill_paid) {
                                return {
                                    id: data.id,
                                    created_on: moment.utc(data.created_on).format('YYYY-MM-DD'),
                                    transaction_created_on: moment.utc(data.transaction.date).format('YYYY-MM-DD'),
                                    billed_amount: `$${data.billed_amount.toFixed(2)}`,
                                    item_name: data.item_name,
                                    description: data.description
                                };
                            }
                        }), 
                        raw_data: response.data.bills
                    }
                }));

            }
        }).catch((error) => {
            if (error.response) {
                if (error.response.data.code == 401) {
                 //Here another http request is made to my api in order to get new auth tokens before retrying the original request. 
                }

        });
    }
    render(){
        const {user} = this.props,
              {activity_day_period, date_filter, dateAccordianOpen, data_grid} = this.state,
              start_date_obj = new Date(),
              end_date_obj = new Date();
              start_date_obj.setDate(start_date_obj.getDate() - 1);

        return(
            <div className="container pl-0 pr-0">
                <div className="row">
                    <div className="col-md-6">
                        <h2>Transaction History</h2>
                        <p>You can check past and pending tranactions here.</p>
                    </div>
                </div>
                <div className="row">
                    <div className="col-md-7 ml-5 mt-3">
                        <h4>Account Balance</h4>
                        <h1 className="display-3 text-muted text-center">${user.account_balance > 0 ? user.account_balance.toFixed(2) : '0.00'}</h1>
                        {user.account_balance > 0 ? (<div className="row"><Link className="ml-auto" to="/transactions/billing/pay">Pay Now <i className="fa fa-chevron-right" /></Link></div>) : (<p className="text-right text-muted"><em>No balances to pay right now. HOORAY!</em></p>)}
                    </div>
                </div>
                <div className="row mt-4">
                    <div className="col-md-3 form-group">
                        <label htmlFor="activity">Activity</label>
                        <select id="activity" name="activity_day_period" className="form-control" value={activity_day_period} onChange={(e) => this.setState({activity_day_period: e.target.value}, this.getTransactionData)}>
                            <option key="1">30 days</option>
                            <option key="2">3 months</option>
                            <option key="3">6 months</option>
                            <option key="4">1 year</option>
                            <option key="5">All</option>
                        </select>
                    </div>
                </div>
                <div className="row pb-3">
                    <div className="ag-blue dashboard-transaction-grid">
                        <AgGridReact columnDefs={data_grid.columns} 
                                     rowData={data_grid.rows} 
                                     groupHeaders="true" 
                                     onGridReady={this.onGridReady}
                                     onGridSizeChanged={this.handleGridResize}/>
                    </div>
                </div>
            </div>
        );
    }
}
4

0 回答 0