React / Redux Async不等待返回的响应

时间:2017-04-21 09:10:28

标签: reactjs redux react-redux

首先,我的代码 正在工作(所有内容都正确导出等),但它并没有等待异步返回数据。 我正在使用 redux-thunk 作为我的异步中间件

我有一个名为 async.js

的动作
export function itemsHasErrored(bool) {
    return {
        type: 'ITEMS_HAS_ERRORED',
        hasErrored: bool
    };
}

export function itemsIsLoading(bool) {
    return {
        type: 'ITEMS_IS_LOADING',
        isLoading: bool
    };
}

export function itemsFetchDataSuccess(items) {
    return {
        type: 'ITEMS_FETCH_DATA_SUCCESS',
        items
    };
}

export function itemsFetchData(url) {
    return (dispatch) => {
        dispatch(itemsIsLoading(true));

        fetch(url)
            .then((response) => {
                if (!response.ok) {
                    throw Error(response.statusText);
                }

                dispatch(itemsIsLoading(false));

                return response;
            })
            .then((response) => response.json())
            .then((items) => dispatch(itemsFetchDataSuccess(items)))
            .catch(() => dispatch(itemsHasErrored(true)));
    };
}

我的减速机

export function itemsHasErrored(state = false, action) {
    switch (action.type) {
        case 'ITEMS_HAS_ERRORED':
            return action.hasErrored;

        default:
            return state;
    }
}

export function itemsIsLoading(state = false, action) {
    switch (action.type) {
        case 'ITEMS_IS_LOADING':
            return action.isLoading;

        default:
            return state;
    }
}

export function items(state = [], action) {
    switch (action.type) {
        case 'ITEMS_FETCH_DATA_SUCCESS':
            return action.items;

        default:
            return state;
    }
}

我有一个容器组件 asyncContainer.js

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux'

import {itemsFetchData} from '../../../actions/async';
import AsyncUI from './asyncUI'

class AsyncContainer extends Component {

    componentDidMount() {
        this.props.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
    }

    render() {

        if (this.props.hasErrored) {
            return <p>Sorry! There was an error loading the items</p>;
        }

        if (this.props.isLoading) {
            return <p>Loading…</p>;
        }
//This fails to wait
        return (
               <AsyncUI />
        );
    }
}

AsyncContainer.propTypes = {
    fetchData: PropTypes.func.isRequired,
    items: PropTypes.array.isRequired,
    hasErrored: PropTypes.bool.isRequired,
    isLoading: PropTypes.bool.isRequired
};

const mapStateToProps = (state) => {
    return {
        items: state.items,
        hasErrored: state.itemsHasErrored,
        isLoading: state.itemsIsLoading
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        fetchData: (url) => dispatch(itemsFetchData(url))
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(AsyncContainer);

最后我有一个名为 asyncUI.js 的简单UI组件以函数方式编写

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux'

const AsyncUI = (items) => {

    return (
        <ul>
            {items.map((item) => (
                <li key={item.id}>
                    {item.label}
                </li>
            ))}
        </ul>
    );
}

const mapStateToProps = (state) => {
    return {
        items: state.items
    };
};

export default connect(mapStateToProps)(AsyncUI);

asyncContainer.js 中,您可以看到对我的简单UI组件的调用

 return (
               <AsyncUI />
        );

但是在调用 asyncUI.js 中的redux store items 的属性为空数组时,items.map失败

但是,如果我从 asyncUI.js 中删除代码并将其放在 asyncContainer.js 中,则可以使用

这是 asyncContainer.js中的代码

  class AsyncContainer extends Component {
        componentDidMount() {
            this.props.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
        }

        render() {
            if (this.props.hasErrored) {
                return <p>Sorry! There was an error loading the items</p>;
            }

            if (this.props.isLoading) {
                return <p>Loading…</p>;
            }

//THIS IS WHERE I HAD <Async />
            return (
                <ul>
                    {this.props.items.map((item) => (
                        <li key={item.id}>
                            {item.label}
                        </li>
                    ))}
                </ul>
            );
        }
    }

    AsyncContainer.propTypes = {
        fetchData: PropTypes.func.isRequired,
        items: PropTypes.array.isRequired,
        hasErrored: PropTypes.bool.isRequired,
        isLoading: PropTypes.bool.isRequired
    };

    const mapStateToProps = (state) => {
        return {
            items: state.items,
            hasErrored: state.itemsHasErrored,
            isLoading: state.itemsIsLoading
        };
    };

    const mapDispatchToProps = (dispatch) => {
        return {
            fetchData: (url) => dispatch(itemsFetchData(url))
        };
    };

    export default connect(mapStateToProps, mapDispatchToProps)(AsyncContainer);

我认为问题是组件 在项目数据准备好之前呈现。这是正常的反应行为。那么我如何“推迟”渲染。正如您所看到的,我正在尝试使用容器/组件样式的体系结构。我总是可以使用上面提到的解决方案,但我想坚持使用这个容器/组件。 我是否需要深入研究Promises等? 我不应该使用asyncUI.js的功能写作方式吗? 我有点困惑。

3 个答案:

答案 0 :(得分:2)

尝试:

const AsyncUI = ({items}) => {
              /* ^ see ^ */
    return (
        <ul>
            {items.map((item) => (
                <li key={item.id}>
                    {item.label}
                </li>
            ))}
        </ul>
    ); }

这会从你在items函数中反应的道具中取出mapStateToProps值,这是一个对象,而不是数组(因此没有map函数)。

注意:这应该可以解决您的问题,但它仍然技术上尝试在2个实例中准备好之前呈现这些项目:

  1. 组件首次渲染。 itemsIsLoading的初始状态为false,因此第一次渲染将失败所有安全检查。 items的默认值为[],因此在调度<ul></ul>操作之前,它应该只显示itemsIsLoading(true)一小段时间。您可以将初始状态设置为true以停止此操作,或将加载检查更改为

    if (this.props.isLoading || this.props.items.length != 0) {
        return <p>Loading…</p>;
    }
    

    可以对这些解决方案的实际必要性进行论证。

  2. fetch返回订单后,调度操作将导致另一个简短的呈现<ul></ul>,因为在设置项目之前加载状态设置为false 。有关解决此问题的一种方法,请参阅dgrijuela's answer。另一种方法是不分派单独的操作,并使ITEMS_FETCH_DATA_SUCCESSITEMS_HAS_ERRORED操作也将itemsIsLoading值更改为false(多个reducer可以对同一操作类型起作用)。

答案 1 :(得分:0)

您在dispatch(itemsIsLoading(false))

之前致电dispatch(itemsFetchDataSuccess(items))

试试这样:

// async.js
...

export function itemsFetchData(url) {
  return (dispatch) => {
    dispatch(itemsIsLoading(true));

    fetch(url)
        .then((response) => {
            if (!response.ok) {
                throw Error(response.statusText);
            }

            return response;
        })
        .then((response) => response.json())
        .then((items) => {
          dispatch(itemsFetchDataSuccess(items));
          dispatch(itemsIsLoading(false));
        })
        .catch(() => dispatch(itemsHasErrored(true)));
    };
}

答案 2 :(得分:0)

请参阅Michael Peyper一个好的答案

事实证明,问题在于我的 asyncUI 组件的编码功能样式。我把它转换回了标准&#39;有状态的组件和它有效的宾果。

asyncContainer.js

class AsyncContainer extends Component {

    componentDidMount() {
        this.props.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
    }

    render() {

          if (this.props.hasErrored) {
                  return <p>Sorry! There was an error loading the items</p>;
              }

              if (this.props.isLoading) {
                  return <p>Loading…</p>;
              }
              return (
                  <AsyncUI />     
        )

    }
}

asyncUI.js

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux'

    class AsyncUI extends Component {

        render() {
                return (
                <ul>
                {this.props.items.map((item) => (
                    <li key={item.id}>
                        {item.label}
                    </li>
                ))}
            </ul>   
            )
        }
    }

    const mapStateToProps = (state) => {
        return {
            items: state.items,
        };
    };

export default connect(mapStateToProps)(AsyncUI);

希望这可以帮助任何人: - )