gpt4 book ai didi

javascript - 使用 fetch api 的 Redux 异步请求

转载 作者:行者123 更新时间:2023-11-29 21:33:36 27 4
gpt4 key购买 nike

我陷入了一种我无法真正调试的奇怪行为。

商店发送执行登录请求传递用户名和密码的操作。然后,当响应准备就绪时,我将凭据存储在 redux 存储中。当我需要执行授权请求时,我在 header 请求中设置这些参数。当我收到响应时,我会使用从响应中获得的新凭据更新商店中的凭据。当我尝试执行第三个请求时,它将未经授权做出响应。我发现这是因为传递给我的 Action 生成器 setCredentials 的所有参数都是空的。我不明白为什么还因为如果我在我的 setCredentials 函数的返回语句之前添加一个调试器并且我在重新开始执行之前等待几秒钟我发现参数不再为空。我在考虑这样一个事实,即请求是异步的,但在 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)

最佳答案

当我问的时候我应该澄清一下

Have you verified that all your endpoints return those headers and not just the login one? Maybe when you performRequest(getUsers), it comes back with empty headers.

我不只是指服务器逻辑。我的意思是在 DevTools 中打开“网络”选项卡并实际验证您的响应是否包含您期望的 header 。事实证明,getUsers() header 并不总是包含凭据:

既然我们已经确认会发生这种情况,让我们看看原因。

您同时发送 submitLogin()performRequest(getUsers) 大致。在重现错误的情况下,问题出在以下步骤序列中:

  1. 您启动 submitLogin()
  2. 您在 submitLogin() 返回之前触发 performRequest(getUsers)
  3. submitLogin() 返回并存储来自响应 header 的凭据
  4. performRequest(getUsers) 返回,但因为它在凭据可用之前启动,服务器以空 header 响应,并且存储这些空凭据而不是现有凭据
  5. performRequest(getWorks) 现在无需凭据即可请求

这个问题有几个修复方法。

不要让旧的未经授权的请求覆盖凭证

我认为用空凭证覆盖现有的良好凭证真的没有意义,对吗?您可以在发送前在 performRequest 中检查它们是否为空:

const performRequest = (api) => (dispatch, getState) => {
return api()
.then(response => {
if (response.headers.get('access-token')) {
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))
},
);
}

或者,您可以忽略 reducer 本身的无效凭据:

case SET_CREDENTIALS:
if (action.credentials['access-token']) {
return Object.assign({}, state, {credentials: action.credentials})
} else {
return state
}

两种方式都可以,具体取决于对您来说更有意义的约定。

在执行请求之前等待

在任何情况下,您是否真的想在获得凭据之前触发 getUsers() ?如果没有,则仅在凭据可用之前触发请求。像这样:

store.dispatch(submitLogin()).then(() => {
store.dispatch(performRequest(getUsers))
store.dispatch(performRequest(getWorks))
})

如果它并不总是可行,或者你想要更复杂的逻辑,比如重试失败的请求,我建议你看看 Redux Saga这让您可以使用强大的并发原语来安排此类工作。

关于javascript - 使用 fetch api 的 Redux 异步请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35381276/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com