我一直在研究一个涉及通过 asyncThunk 调用上传文件的调用的问题。调用完成后,它会返回文件路径的有效负载。
从那里,我尝试在 uploadFileAsyncThunk.fulfilled 时更新“addCase”代码块中名为“transaction”的对象的状态,以便可以将其保存在数据库中。一切都按其应有的方式运行,除了代码执行的顺序不是我期望的那样。当事务对象发送到第二个 asyncThunk (postTransactionThunk) 时,状态似乎没有更新。
通过代码调试,我可以看到 uploadResultAction 填充了我期望的返回数据。并且
uploadFileThunk.fulfilled.match(uploadResultAction)
对于继续执行代码的其余部分是有效的。从那里,获取 filePathParam 确实包含我想要的数据,并且分派数据确实有效。我可以在组件顶部控制台记录我的数据已更新。然而,当我通过 Redux DevTools 查看状态时。我的状态在 dispatchPostTransactionThunk
执行之前不会更新,此时为时已晚,因为 POST 引用的事务数据已过时。
如何更新我的
transaction
状态,以便能够使用最新数据执行 postTransactionThunk?
const MyComponent = () => {
const transaction = useAppSelector(
(state) => state.transaction.transaction
);
const handleClick = async (e) => {
e.preventDefault();
// returned value is accurate
const uploadResultAction = await dispatch(uploadFileThunk(file));
// condition passes and enters code block
if (uploadFileThunk.fulfilled.match(uploadResultAction)) {
// filePathParam has the payload I want.
const filePathParam =
{
filePath: uploadResultAction.payload.response,
};
// dispatch executes updating transaction.filePath.
dispatch(updateTransaction(filePathParam));
// ISSUE: transaction does not have the transaction.filePath updated; filePath is still an empty string.
const postTransactionResultAction = await dispatch(postTransactionThunk(transaction));
if (postTransactionThunk.fulfilled.match(
postTransactionResultAction)) {
// ...
}
}
}
return (
<div>
<Button onClick={handleSubmit}>Click</Button>
</div>
);
}
export default MyComponent;
以下是完成交易后更新交易的补充逻辑:
const initialTransactionState = {
filePath: '',
};
const initialState = {
transaction: initialTransactionState,
fileUploadResponse: null,
loading: false,
error: null,
};
export const postTransactionThunk = createAsyncThunk(
'postTransactionThunk',
async (transaction, { dispatch }) => {
const response = await dispatch(
transactionApiSlice.endpoints.postTransaction.initiate(transaction)
);
return response.data;
}
);
export const uploadFileThunk = createAsyncThunk(
'uploadFileThunk',
async (file, { dispatch }) => {
const response = await dispatch(
fileApiSlice.endpoints.postFile.initiate(file)
);
return response.data;
}
);
export const transactionSlice = createSlice({
name: 'transaction',
initialState,
reducers: {
updateTransaction: (state, action) => {
return {
...state,
transaction: { ...state.transaction, ...action.payload },
};
},
},
extraReducers: (builder) => {
builder
.addCase(uploadFileThunk.fulfilled, (state, action) => {
return {
...state,
loading: false,
error: null,
fileUploadResponse: action.payload,
transaction: {
...state.transaction,
filePath: action.payload.response,
},
};
})
},
});
export const {
updateTransaction,
} = transactionSlice.actions;
export default transactionSlice.reducer;
我尝试过通过代码进行调试。我还尝试在每个调度调用之间放置级联 .then(...) 块,这也没有什么区别。
问题似乎是由
transaction
操作中流更新并传递给 updateTransaction
的 postTransactionThunk
值的陈旧闭包。
我建议更新
postTransactionThunk
以直接访问当前状态值,而不是依赖于可能已过时的传递值。在 thunk 中,您可以使用 thunkApi.getState
访问 current 状态值
export const postTransactionThunk = createAsyncThunk(
'postTransactionThunk',
async (_, { dispatch, getState }) => {
const state = getState();
const transaction = state.transaction.transaction;
const { data } = await dispatch(
transactionApiSlice
.endpoints
.postTransaction
.initiate(transaction)
).unwrap();
return data;
}
);
UI 代码不需要具有当前
transaction
状态,请使用 dispatch(postTransactionThunk())
。
const uploadResultAction = await dispatch(uploadFileThunk(file));
if (uploadFileThunk.fulfilled.match(uploadResultAction)) {
const filePathParam = {
filePath: uploadResultAction.payload.response,
};
dispatch(updateTransaction(filePathParam));
const postTransactionResultAction = await dispatch(postTransactionThunk());
if (
postTransactionThunk.fulfilled.match(postTransactionResultAction)
) {
// ...
}
}
我建议也充分发挥 Thunk 的潜力。您可以等待并解开 thunk 结果,而不是在本地使用匹配器。类似于以下内容:
try {
const { response: filePath } = await dispatch(uploadFileThunk(file)).unwrap();
const filePathParam = { filePath };
dispatch(updateTransaction(filePathParam));
await dispatch(postTransactionThunk()).unwrap();
// ... postTransactionThunk success, keep going
} catch(error) {
// rejected Promise, thrown error, postTransactionThunk failure
}