【问题标题】:How to make the second request wait for the previous one and use its data?如何让第二个请求等待前一个请求并使用其数据?
【发布时间】:2019-11-13 01:04:54
【问题描述】:

有 2 个 react 组件,都向服务器发送请求(使用 componentDidMount)每个请求查看令牌是否太旧,如果是,则使用 refreshToken 更新它。

问题 - 第一个请求将成功更新令牌,将返回一个新令牌和一个新的 refreshToken。第二个请求将使用旧的 refreshToken 发送到服务器,服务器将产生错误。

解决方案是声明一个全局变量(window.pending)。如果为 false - 我们不更新令牌,如果令牌已更新 - 为其分配一个承诺,等待其完成并应用生成的令牌。

// From component 1

loadClient = async () => {
  const token = await this.props.getToken();
  // other code
};

// From component 2

loadProject = async () => {
  const token = await this.props.getToken();
  // other code
};

// НОС

const tokenProviderHOC = WrappedComponent =>
  class extends PureComponent<Props> {
    request = null;

    componentWillUnmount() {
      if (this.request !== null) this.request.abort(); //Cancel server request
    }

    checkToken = userId => async () => {
      const { history } = this.props;

      // token, refreshToken, expiresIn from localStorage
      const token = methods.getToken("token");
      const refreshToken = methods.getRefreshToken("refreshToken");
      const expiresIn = methods.getExpiresIn("expiresIn");

      if (Date.now() >= expiresIn) {
        if (window.pending === false) {
          console.log("we are first");

          let res;

          this.request = this._updateToken({ refreshToken, userId });

          window.pending = new Promise(async resolve => {
            console.log("request start");

            res = await this.request.ready(); //ready - start server request
            window.pending = false;

            console.log("request end", res ? res.token : false);
            return res ? resolve(res.token) : resolve(false);
          });
        }
        console.log("Token is already looking");
        return window.pending;
      }
      return token;
    };

    render() {
      const { userId, history, ...props } = this.props;

      return <WrappedComponent {...props} getToken={this.checkToken(userId)} />;
    }
  };

const mapStateToProps = state => ({
  userId: userSelectors.getUserInfo(state)?.id || null,
});

export const tokenProvider = compose(
  withRouter,
  connect(mapStateToProps),
  tokenProviderHOC,
);

必须在控制台中显示:

  • '我们是第一'
  • '请求开始'或'令牌已经在寻找'
  • '请求结束'

现在控制台显示:

  • '我们是第一'
  • '请求开始'
  • '令牌已经在寻找'
  • '请求结束'
  • '我们是第一'
  • '请求开始'
  • '令牌已经在寻找'
  • tokenProvider.js:121 PUT http://site.loc/user/1/send net::ERR_ABORTED 400(错误请求)
    // 因为令牌是另一个
  • 请求结束错误'

似乎第二个请求正在等待第一个请求,然后再次尝试从服务器中找出令牌并将旧的 refreshToken 传递给它。如何解决问题?

【问题讨论】:

  • 我认为你应该把你的 2 个请求放在 HoC 组件中。然后将第二个请求包装在第一个请求中。
  • 不明白。怎么样?

标签: reactjs


【解决方案1】:

忽略 HOC 的东西,因为它在这里并不重要,这样的东西应该可以工作。这个想法是你有一个单例的 Promise,它最终会解析为一个新的令牌。

此外,您也可以使用(模块)全局变量,除非您特别需要,否则无需在 window 中存储任何内容。

tokenIsValid_updateToken 应该或多或少不言自明,此处省略了错误处理。

let fetchTokenPromise = null;
let currentToken = null;

function getToken() {
  if (currentToken && tokenIsValid(currentToken)) {
    // Current token is valid, return it (as a promise).
    return Promise.resolve(currentToken);
  }

  if (!fetchTokenPromise) {
    // We're not fetching a token, start fetching one.
    fetchTokenPromise = new Promise(async (resolve, reject) => {
      const token = await _updateToken(currentToken);
      currentToken = token; // Update saved token.
      fetchTokenPromise = null; // Clear fetching state.
      resolve(token);
    });
  }
  return fetchTokenPromise;
}

// ...

const token = await getToken();  // this will either get the current token, or start refreshing one

【讨论】:

    猜你喜欢
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 2022-01-21
    • 2016-09-06
    • 1970-01-01
    相关资源
    最近更新 更多