首页 > 解决方案 > React 需要点击两次才能渲染从 api 获取的数据

问题描述

我需要在链接到当前页面的按钮上单击两次,以便我可以获取从 api 获取的数据以进行渲染。我正在使用 nivo / 图表来可视化我的数据。

该组件从 api 获取公司列表,然后第二个 fetch 循环遍历每个结果,为每个不同的公司获取数据。

在第一次尝试时,公司列表是在父组件上获取的,之后每个子组件都会发生一个获取请求 (parent=list of chart components, child=Company Chart),但是在分页过程中它没有正确呈现,所以我不得不将状态提升到父组件,这次的问题是父组件在第一次单击时没有呈现,我不得不双击例如链接按钮,以便父组件能够呈现。

我认为问题可能正在发生,因为可能存在与 componentDidMount 操作顺序的错误同步,因为我确信第一个和第二个数据获取(首先是公司获取请求和第二个不同的公司获取请求)同时执行,而不是而不是一个接一个。所以我转向 redux 并将我的应用程序构建为 redux 规则。它没有解决任何问题,仍然需要双击链接才能进行渲染。

现在我觉得我需要为 api 获取过程添加一些等待/异步规则,但我不确定这是否可行,所以我非常感谢就如何解决这个小问题获得第二意见,因为它有困扰我好几个星期了。

我的减速机:

import { FETCH_COMPANIES } from '../actions/types';

const initialState = {
    next : null,
    prev : null,
    items : [],
    item : [],
}



export default function(state = initialState, action) {
    switch (action.type) {
        case FETCH_COMPANIES:
            return {
                ...state,
                items : action.payload.companies,
                next : action.payload.next,
                prev : action.payload.prev,
                
            }

        default:
            return state;
    }
}

我的 Store.js:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';


const initialState = {};
const middleware = [thunk];

const store = createStore(
    rootReducer,
    initialState,
    compose(
        applyMiddleware(...middleware)
    )
)


export default store;

我的行动:

import axios from 'axios';
import { FloatType } from 'three';
import { FETCH_COMPANIES } from './types'; 





export const fetchAllData = (url) => dispatch => {
    fetch(url)
    .then(res => res.json())
    .then(
        posts => 
        
        dispatch({
            type : FETCH_COMPANIES,
            payload : FetchCall(posts),
        }) 
    )
    

}

function FetchCall(res) {
    let next;
    let prev;
    try {
        next = res.next;
    }
    catch(err) {
        console.log(err)
    }
    try {
        prev = res.previous;
    }
    catch(err) {
        console.log(err)
    }

    const CompanyArray = Array()

    res.results.map(element => {
        axios.get(`https://API/${element.symbol}/`).then((res) => {
            const DataGroup = handleChartData(res.data)
            CompanyArray.push({
                'name' : element.symbol,
                'data' : DataGroup,
            })
        })
        
    });

    const ALL_DATA = {
        'next' : next,
        'prev' : prev,
        'companies' : CompanyArray,
    }

    return ALL_DATA;

}



function handleChartData(data) {
    DataGroup = Object()
    return DataGroup;
}

我的组件:

import React, { useState}  from 'react';
import { Card, Row, Col, Button } from 'antd';
import Chart from '../components/Chart';
import DetailCompany from './CompanyDetail';
import { connect } from 'react-redux';
import { fetchAllData } from '../actions/chartActions';
import PropTypes from 'prop-types';

class CompanyList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            charts : this.props.charts
        }
    }
    
    componentWillMount() {

        try {
            this.props.fetchAllData("https://API/company/")
        }
        catch(err) {
            console.log(err)
        }
        
    };

 
    prevPage = () => {
        let toPage = this.props.prev
        this.props.fetchAllData(toPage)
       
    }
    nextPage = () => {
        let toPage = this.props.next
        
        this.props.fetchAllData(toPage)
        
    }

    

    

    
    render() {
        
        const chartItems = this.state.charts.map(chart => (
            <Col style={{margin:'0 0 75px 0'}} span={12} key={chart.name}>
                <h1 style={{lineHeight:'2em', margin:'0 0 0 70px'}}>{chart.name}</h1>
                <div className="chart-block">
                        
                        <Chart symbol={chart.name} 
                                data={chart.data.chartData}
                      >
                               
                        </Chart>       
                </div>
            </Col>
        ));
        return (
            <Card>
                <Row>
                    {chartItems}
                </Row>    
                <Row>
                    <Button disabled={(this.props.prev ? false : true )} onClick={() => {this.prevPage()}}>Previous</Button>
                    <Button onClick={() => {this.nextPage()}}>Next</Button>
                </Row>
            </Card>
        )
    }
}


CompanyList.propTypes = {
    fetchAllData : PropTypes.func.isRequired,
    charts : PropTypes.array.isRequired,
}

const mapStateToStore = state => ({
    prev : state.charts.prev,
    next : state.charts.next,
    charts : state.charts.items,
});

export default connect(mapStateToStore, { fetchAllData })(CompanyList);

如果有人能帮助我解决这个问题并理解它以防止进一步的误导或再次发生,我将不胜感激。谢谢你。

标签: javascriptreactjsreduxfetch-api

解决方案


您的 fetch thunk 不太正确。特别是,这一行:

payload : FetchCall(posts),

FetchCall是异步的,但您不会在调度之前等待它完成。在axios 调用完成之前,FetchCall您将返回ALL_DATA一个空值。CompanyArray

return您需要在ing 或dispatching之前完成所有 fetch 调用。您可以使用Promise/执行此操作then,但我发现使用async/更容易await。无论哪种方式,您都需要Promise.all解析整个数组。(另外我不知道你为什么axios在一个地方和另一个地方使用fetch?)。

// helper function to fetch the data for one company
const getCompanyData = async (symbol) => {
  const res = await axios.get(`https://API/${symbol}/`);
  return {
    name: symbol,
    data: res.data,
  }
}

export const fetchAllData = (url) => async (dispatch) => {
  const res = await axios.get(url);
  const posts = res.data;
  const {next, prev, results} = posts;

  const companyArray = await Promise.all(
    results.map( element => getCompanyData(element.symbol) )
  );

  dispatch({
    type : FETCH_COMPANIES,
    payload: {
      next,
      prev,
      companyArray,
    }
  });
}

推荐阅读