如何在每个Relay.js网络请求上添加加载指示符

时间:2016-10-12 16:53:56

标签: javascript reactjs networking relayjs

我目前正在构建一个Relay / React网络应用。除了一个例外,它非常简单。当我的任何组件发出网络请求时,我无法弄清楚如何获得全局通知。当有网络活动的时候,我正在跳到我的应用程序顶部添加一个微调器,因为我的一些突变需要很长时间才能加载。

这是我尝试解决此问题,但它仅适用于新路由页面加载。

Consumer<BigDecimal>

理想情况下,我可以获得一些回调,我可以传递给我的App组件,它会呈现我的所有页面页眉和页脚。任何有关这方面的帮助将不胜感激。我已经在互联网上待了一段时间,试图找到一个很好的解决方案。

3 个答案:

答案 0 :(得分:1)

您还可以尝试添加自定义Relay Network Layer,在每个请求中呈现加载指示符组件。我认为主要考虑的是全球&#34;加载指标不仅涉及指标的设计,还涉及其对全球UI的影响。它会阻止UI吗?只是它的一部分吗?它会向上/向下移动其他元素吗?等等。

与此同时,您可以执行以下操作:

  handleSubmit(event) {
    event.preventDefault();
    this.setState({isLoading: true});

    this.props.relay.commitUpdate(
      new LoginMutation({
        email: this.refs.email.value,
        password: this.refs.password.value
      }), {
        onSuccess: response => {
          Auth.login(response.login.accessToken);
          const { location, router } = this.props;

          if (location.state && location.state.nextPathname) {
            router.replace(location.state.nextPathname)
          } else {
            router.replace('/')
          }
        },
        onFailure: transaction => {
          this.setState({hasError: true});
          this.setState({isLoading: false});
        }
      }
    );
  }

上面的摘录摘自here。您可以在该回购中看到其余的逻辑。

答案 1 :(得分:0)

您可以在react中创建自定义微调器组件,并根据您的数据加载状态显示或隐藏微调器。

这方面的一个例子可以是 -

你的Spinner组件可能是这样的 -

    let SpinMe
    = (
        <div className="spinner-container">
            <div className="loader">
                <svg className="circular">
                    <circle className     = "path"
                        cx                = "50"
                        cy                = "50"
                        r                 = "20"
                        fill              = "none"
                        strokeWidth      = "3"
                        strokeMiterLimit = "10"
                    />
                </svg>
            </div>
        </div>
    );

此微调器组件应比其他组件稍高z-index,以便在加载时,用户无法单击其他组件或与其他组件交互。

同样在造型中展示了一些透明的深色背景。

例如

.spinner-container {

    position         : absolute;
    background-color : rgba(12, 12, 12, 0.61);
    width            : 100%;
    min-height       : 100%;
    background-size  : 100% 100%;
    text-align       : center;
    z-index          : 3;
    top              : 0;
    left             : 0;
}

现在您想要使用微调器的另一个组件,并且您希望在此组件中发出网络请求。

import React from 'react';

class SpinTestComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        isLoading:false
    };
  }

  sendNetworkRequest(URL, dataToSend) {
      return $.ajax({
            type        : 'POST',
            url         : URL,
            data        : JSON.stringify(dataToSend),
            dataType    : 'json'
            });
  }

  componentDidMount() {
        const URL = "test url";
        const dataToSend = { param1:"val", param2:"val" };

        this.setState({isLoading:true});
        this.sendNetworkRequest(dataToSend)
            .then(
            () => {
                // success response now remove spinner
                this.setState({isLoading:false});
            },
            () => {

                // error response again remove spinner and 
                // show error message to end user
                this.setState({isLoading:false});
            });
    }

    render() {
        return (
                <div>
                { this.state.isLoading ? <SpinMe/> : null }
                    <div>
                        <h1>
                            Remaining Component structure 
                            or Jsx
                        </h1>
                        <p>
                            To be show after loading is done.
                        </p>
                    </div>
                </div>
               );
    }
}

export default SpinTestComponent;

答案 2 :(得分:0)

我最终扩展了Relay的默认网络层,以便我可以制作一些我的主要根组件监听的Flux事件。如果我愿意,这允许我将所有加载和错误消息处理在一个地方。

这是我的最终解决方案。不确定它是最干净的,但效果很好。

import Relay from "react-relay";
import RelayMutationRequest from "react-relay/lib/RelayMutationRequest";
import AppDispatcher from "./AppDispatcher";

export default class CustomNetworkLayer extends Relay.DefaultNetworkLayer {

    constructor(uri, init)
    {
        super(uri, init);
    }

    networkLoadingEvent()
    {
        AppDispatcher.dispatch({
            actionType: 'LOADING'
        });
    }

    networkDoneLoadingEvent()
    {
        AppDispatcher.dispatch({
            actionType: 'DONE_LOADING'
        });
    }

    networkErrorEvent(error)
    {
        AppDispatcher.dispatch({
            actionType: 'ERROR',
            payload: error
        });
    }

    sendQueries(requests)
    {
        this.networkLoadingEvent();
        return Promise.all(requests.map(request => (
            this._sendQuery(request).then(
                result => result.json()
            ).then(payload =>
            {
                if (payload.hasOwnProperty('errors')) {
                    const error = CustomNetworkLayer.createRequestError(request, '200', payload);
                    this.networkErrorEvent(payload.errors[0].message);
                    request.reject(error);
                } else {
                    if (!payload.hasOwnProperty('data')) {
                        const error = new Error(
                            'Server response was missing for query ' +
                            `\`${request.getDebugName()}\`.`
                        );
                        this.networkErrorEvent(error);
                        request.reject(error);
                    } else {
                        this.networkDoneLoadingEvent();
                        request.resolve({response: payload.data});
                    }
                }
            }).catch(
                error =>
                {
                    this.networkErrorEvent(error);
                    request.reject(error)
                }
            )
        )));
    }

    sendMutation(request)
    {
        this.networkLoadingEvent();
        return this._sendMutation(request).then(
            result => result.json()
        ).then(payload =>
        {
            if (payload.hasOwnProperty('errors')) {
                const error = CustomNetworkLayer.createRequestError(request, '200', payload);
                this.networkErrorEvent(payload.errors[0].message);
                request.reject(error);
            } else {
                this.networkDoneLoadingEvent();
                request.resolve({response: payload.data});
            }
        }).catch(
            error =>
            {
                this.networkErrorEvent(error);
                request.reject(error)
            }
        );
    }


    /**
     * Formats an error response from GraphQL server request.
     */
    static formatRequestErrors(request, errors)
    {
        const CONTEXT_BEFORE = 20;
        const CONTEXT_LENGTH = 60;

        const queryLines = request.getQueryString().split('\n');
        return errors.map(({locations, message}, ii) =>
        {
            const prefix = (ii + 1) + '. ';
            const indent = ' '.repeat(prefix.length);

            //custom errors thrown in graphql-server may not have locations
            const locationMessage = locations ?
                ('\n' + locations.map(({column, line}) =>
                {
                    const queryLine = queryLines[line - 1];
                    const offset = Math.min(column - 1, CONTEXT_BEFORE);
                    return [
                        queryLine.substr(column - 1 - offset, CONTEXT_LENGTH),
                        ' '.repeat(Math.max(0, offset)) + '^^^',
                    ].map(messageLine => indent + messageLine).join('\n');
                }).join('\n')) :
                '';

            return prefix + message + locationMessage;

        }).join('\n');
    }

    static createRequestError(request, responseStatus, payload)
    {
        const requestType =
            request instanceof RelayMutationRequest ? 'mutation' : 'query';
        const errorReason = typeof payload === 'object' ?
            CustomNetworkLayer.formatRequestErrors(request, payload.errors) :
            `Server response had an error status: ${responseStatus}`;
        const error = new Error(
            `Server request for ${requestType} \`${request.getDebugName()}\` ` +
            `failed for the following reasons:\n\n${errorReason}`
        );
        error.source = payload;
        error.status = responseStatus;
        return error;
    }
}

然后在我的index.js文件中执行此操作:

Relay.injectNetworkLayer(new CustomNetworkLayer("/graphql",
    {
        fetchTimeout: 35000, // timeout after 35 seconds
        retryDelays: [2000] // retry after 2 seconds
    }));

快速说明:AppDispatcher只是一个flux js调度程序,我正在我的主包装器组件中监听这些事件。

希望这可以帮助其他人。我当然花了太多时间在这上面。

还要感谢帮助我解决这个问题的所有人。