【发布时间】:2016-02-13 14:46:42
【问题描述】:
我陷入了无法真正调试的奇怪行为。
商店调度执行登录请求并传递用户名和密码的操作。然后,当响应准备好时,我将凭据存储在 redux 存储中。当我需要执行授权请求时,我在标头请求中设置这些参数。当我收到响应时,我会使用从响应中获得的新凭据更新商店中的凭据。 当我尝试执行第三个请求时,它将未经授权响应。我发现这是因为传递给我的动作生成器 setCredentials 的所有参数都是空的。我也无法理解为什么,因为如果我在 setCredentials 函数的 return 语句之前添加一个调试器,并在重新启动执行之前等待几秒钟,我发现参数不再为空。我在考虑请求是异步的但在 then 语句中响应应该准备好的事实,对吗?我还注意到 fetch 为每个请求发送了两个请求。 这里的代码更清晰。
import { combineReducers } from 'redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const initialState = {
currentUser: {
credentials: {},
user: {}
},
test: {},
users: []
}
export const SUBMIT_LOGIN = 'SUBMIT_LOGIN'
export const SET_USER = 'SET_USER'
export const TEST = 'TEST'
export const SET_USERS = 'SET_USERS'
export const SET_CREDENTIALS = 'SET_CREDENTIALS'
//actions
const submitLogin = () => (dispatch) => {
return postLoginRequest()
.then(response => {
dispatch(setCredentials(
response.headers.get('access-token'),
response.headers.get('client'),
response.headers.get('expiry'),
response.headers.get('token-type'),
response.headers.get('uid')
));
return response
})
.then(response => {
return response.json();
})
.then(
(user) => dispatch(setUser(user.data)),
);
}
const performRequest = (api) => (dispatch) => {
return api()
.then(response => {
dispatch(setCredentials(
response.headers.get('access-token'),
response.headers.get('client'),
response.headers.get('expiry'),
response.headers.get('token-type'),
response.headers.get('uid')
));
return response
})
.then(response => {return response.json()})
.then(
(users) => {
dispatch(setUsers(users.data))
},
);
}
const setUsers = (users) => {
return {
type: SET_USERS,
users
}
}
const setUser = (user) => {
return {
type: SET_USER,
user
}
}
const setCredentials = (
access_token,
client,
expiry,
token_type,
uid
) => {
debugger
return {
type: SET_CREDENTIALS,
credentials: {
'access-token': access_token,
client,
expiry,
'token-type': token_type,
uid
}
}
}
//////////////
const currentUserInitialState = {
credentials: {},
user: {}
}
const currentUser = (state = currentUserInitialState, action) => {
switch (action.type) {
case SET_USER:
return Object.assign({}, state, {user: action.user})
case SET_CREDENTIALS:
return Object.assign({}, state, {credentials: action.credentials})
default:
return state
}
}
const rootReducer = combineReducers({
currentUser,
test
})
const getAuthorizedHeader = (store) => {
const credentials = store.getState().currentUser.credentials
const headers = new Headers(credentials)
return headers
}
//store creation
const createStoreWithMiddleware = applyMiddleware(
thunk
)(createStore);
const store = createStoreWithMiddleware(rootReducer);
const postLoginRequest = () => {
return fetch('http://localhost:3000/auth/sign_in', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'test@test.com',
password: 'password',
})
})
}
const getUsers = () => {
const autorizedHeader = getAuthorizedHeader(store)
return fetch('http://localhost:3000/users',
{
method: 'GET',
headers : autorizedHeader
}
)
}
const getWorks = () => {
const autorizedHeader = getAuthorizedHeader(store)
return fetch('http://localhost:3000/work_offers',
{
method: 'GET',
headers : autorizedHeader
}
)
}
// this request works fine
store.dispatch(submitLogin())
// this request works fine
setTimeout(() => {
store.dispatch(performRequest(getUsers))
}, 3000)
// this fails
setTimeout(() => {
store.dispatch(performRequest(getWorks))
}, 5000)
【问题讨论】:
-
您是否已验证 all 您的端点返回这些标头而不仅仅是登录标头?也许当你
performRequest(getUsers)时,它会返回空标题。 -
是的。已验证。对我的后端的每个授权请求都会返回这些标头。 @丹
-
唉,很难从代码中猜到。如果您可以创建一个在 JSBin 中重现该问题的独立示例,我可以看看。
-
非常感谢。我会做的。 @丹
-
嗨,丹。我为测试创建了这个repo。谢谢@DanAbramov
标签: javascript fetch race-condition redux es6-promise