以编程方式下载文件,同时使用 Redux 向用户提供反馈
Download a file programmatically while providing feedback to the user using Redux
我有一个后端,returns 一个要下载的文件。
目前,当用户做某事时,会调度一个动作来运行减速器 export
并执行通常的技巧来下载文件。我正在使用 redux-toolkit。
const mySlice = createSlice({
name: "my",
initialState,
reducers: {
export: (state) => {
const link = document.createElement("a")
link.href = '...'
document.body.appendChild(link)
link.click()
link.parentNode?.removeChild(link)
},
}
})
这行得通。但是,我想在下载过程中向用户提供反馈,即模态对话框。所以我在状态中定义了一个isExporting
属性,并且有一个组件显示对话框取决于这个属性。
但是这样写的reducer是不行的:
export: (state) => {
state.isExporting = true
const link = document.createElement("a")
link.href = '...'
document.body.appendChild(link)
link.click()
link.parentNode?.removeChild(link)
state.isExporting = false
},
因为我相信我引入了副作用,而且状态将在 reducer 执行之后设置,而不是在两者之间设置。
正确的做法是什么?
创建一个异步操作 createAsyncThunk
并定义一个带有案例的 extraReducer:
For this example i create async function download
const fakeDownload = async () =>
new Promise((resolve) =>
setTimeout(() => {
const link = document.createElement("a");
link.href = "...";
document.body.appendChild(link);
link.click();
link.parentNode?.removeChild(link);
resolve();
}, 1000) // I set a timeout to emulate a request.
);
创建异步操作:
export const downloadFile = createAsyncThunk(
"download/file",
async (_, thunkApi) => {
try {
return fakeDownload();
} catch (e) {
return thunkApi.rejectWithValue("Impossible to download");
}
}
);
减速器:
const initialState = {
isExporting: false
};
export const appSlice = createSlice({
name: "app",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(downloadFile.pending, (state) => {
state.isExporting = true;
});
builder.addCase(downloadFile.fulfilled, (state) => {
state.isExporting = false;
});
builder.addCase(downloadFile.rejected, (state) => {
// Do something with error
state.isExporting = false;
});
}
});
export const isExportingSelector = (state) => state.app.isExporting;
export default appSlice.reducer;
现在您可以使用选择器和调度操作了。
你可以在这里找到一个活生生的例子:
我有一个后端,returns 一个要下载的文件。
目前,当用户做某事时,会调度一个动作来运行减速器 export
并执行通常的技巧来下载文件。我正在使用 redux-toolkit。
const mySlice = createSlice({
name: "my",
initialState,
reducers: {
export: (state) => {
const link = document.createElement("a")
link.href = '...'
document.body.appendChild(link)
link.click()
link.parentNode?.removeChild(link)
},
}
})
这行得通。但是,我想在下载过程中向用户提供反馈,即模态对话框。所以我在状态中定义了一个isExporting
属性,并且有一个组件显示对话框取决于这个属性。
但是这样写的reducer是不行的:
export: (state) => {
state.isExporting = true
const link = document.createElement("a")
link.href = '...'
document.body.appendChild(link)
link.click()
link.parentNode?.removeChild(link)
state.isExporting = false
},
因为我相信我引入了副作用,而且状态将在 reducer 执行之后设置,而不是在两者之间设置。
正确的做法是什么?
创建一个异步操作 createAsyncThunk
并定义一个带有案例的 extraReducer:
For this example i create async function download
const fakeDownload = async () =>
new Promise((resolve) =>
setTimeout(() => {
const link = document.createElement("a");
link.href = "...";
document.body.appendChild(link);
link.click();
link.parentNode?.removeChild(link);
resolve();
}, 1000) // I set a timeout to emulate a request.
);
创建异步操作:
export const downloadFile = createAsyncThunk(
"download/file",
async (_, thunkApi) => {
try {
return fakeDownload();
} catch (e) {
return thunkApi.rejectWithValue("Impossible to download");
}
}
);
减速器:
const initialState = {
isExporting: false
};
export const appSlice = createSlice({
name: "app",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(downloadFile.pending, (state) => {
state.isExporting = true;
});
builder.addCase(downloadFile.fulfilled, (state) => {
state.isExporting = false;
});
builder.addCase(downloadFile.rejected, (state) => {
// Do something with error
state.isExporting = false;
});
}
});
export const isExportingSelector = (state) => state.app.isExporting;
export default appSlice.reducer;
现在您可以使用选择器和调度操作了。
你可以在这里找到一个活生生的例子: