有2个React组件,两个组件都将请求发送到服务器(带有componentDidMount)。每个请求都会查看令牌是否过旧;如果是,则使用refreshToken对其进行更新。
问题-第一个请求将成功更新令牌,将返回新令牌和新的refreshToken。第二个请求将使用旧的refreshToken发送到服务器,服务器将生成错误。
解决方案是声明一个全局变量(window.pending)。如果为假-我们不更新令牌,如果令牌已更新-为它分配一个承诺,等待其完成并应用生成的令牌。
// 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,
);
必须显示在控制台中:
现在控制台显示:
似乎第二个请求正在等待第一个请求,然后再次尝试从传递旧的refreshToken的服务器中查找令牌。如何解决问题?
答案 0 :(得分:0)
忽略HOC内容,因为在这里并不重要,类似的事情应该起作用。这样的想法是,您有一个承诺,该承诺将最终解析为一个新令牌。
此外,您也可以使用(模块)全局变量,除非特别需要,否则无需将任何内容存储在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