Redux 传奇、axios 和进度事件
Redux saga, axios and progress event
有没有clean/short/right方法可以同时使用axios promise和上传进度事件?
假设我有下一个上传功能:
function upload(payload, onProgress) {
const url = '/sources/upload';
const data = new FormData();
data.append('source', payload.file, payload.file.name);
const config = {
onUploadProgress: onProgress,
withCredentials: true
};
return axios.post(url, data, config);
}
这个函数返回了承诺。
我也有一个传奇:
function* uploadSaga(action) {
try {
const response = yield call(upload, payload, [?? anyProgressFunction ??]);
yield put({ type: UPLOADING_SUCCESS, payload: response });
} catch (err) {
yield put({ type: UPLOADING_FAIL, payload: err });
}
}
我想接收进度事件并按saga 放置。我还想捕获 axios 请求的成功(或失败)结果。可能吗?
谢谢。
所以我找到了答案,谢谢 Mateusz Burzyński 的澄清。
我们需要使用 eventChannel,但有点巧妙。
假设我们有api上传文件的功能:
function upload(payload, onProgress) {
const url = '/sources/upload';
const data = new FormData();
data.append('source', payload.file, payload.file.name);
const config = {
onUploadProgress: onProgress,
withCredentials: true
};
return axios.post(url, data, config);
}
在 saga 中我们需要创建 eventChannel 但将 emit 放在外面。
function createUploader(payload) {
let emit;
const chan = eventEmitter(emitter => {
emit = emitter;
return () => {}; // it's necessarily. event channel should
// return unsubscribe function. In our case
// it's empty function
});
const uploadPromise = upload(payload, (event) => {
if (event.loaded.total === 1) {
emit(END);
}
emit(event.loaded.total);
});
return [ uploadPromise, chan ];
}
function* watchOnProgress(chan) {
while (true) {
const data = yield take(chan);
yield put({ type: 'PROGRESS', payload: data });
}
}
function* uploadSource(action) {
const [ uploadPromise, chan ] = createUploader(action.payload);
yield fork(watchOnProgress, chan);
try {
const result = yield call(() => uploadPromise);
put({ type: 'SUCCESS', payload: result });
} catch (err) {
put({ type: 'ERROR', payload: err });
}
}
我个人认为接受的答案非常令人费解,而且我很难实施它。其他 google / SO 搜索都导致了类似类型的答案。如果它对你有用,那很好,但我找到了另一种使用 EventEmitter
的方法,我个人认为它更简单。
在您的代码中的某处创建一个事件发射器:
// emitter.js
import { EventEmitter } from "eventemitter3";
export default new EventEmitter();
在您进行 api 调用的 saga 中,使用此发射器在 onUploadProgress
回调中发出一个事件:
// mysagas.js
import eventEmitter from '../wherever/emitter';
function upload(payload) {
// ...
const config = {
onUploadProgress: (progressEvent) = {
eventEmitter.emit(
"UPLOAD_PROGRESS",
Math.floor(100 * (progressEvent.loaded / progressEvent.total))
);
}
};
return axios.post(url, data, config);
}
然后在你需要这个上传进度号的组件中,你可以在挂载上监听这个事件:
// ProgressComponent.jsx
import eventEmitter from '../wherever/emitter';
const ProgressComponent = () => {
const. [uploadProgress, setUploadProgress] = useState(0);
useEffect(() => {
eventEmitter.on(
"UPLOAD_PROGRESS",
percent => {
// latest percent available here, and will fire every time its updated
// do with it what you need, i.e. update local state, store state, etc
setUploadProgress(percent)
}
);
// stop listening on unmount
return function cleanup() {
eventEmitter.off("UPLOAD_PROGRESS")
}
}, [])
return <SomeLoadingBar value={percent} />
}
这对我有用,因为我的应用程序出于其他原因已经在使用全局 eventEmitter
。我发现这更容易实现,也许其他人也会。
有没有clean/short/right方法可以同时使用axios promise和上传进度事件?
假设我有下一个上传功能:
function upload(payload, onProgress) {
const url = '/sources/upload';
const data = new FormData();
data.append('source', payload.file, payload.file.name);
const config = {
onUploadProgress: onProgress,
withCredentials: true
};
return axios.post(url, data, config);
}
这个函数返回了承诺。
我也有一个传奇:
function* uploadSaga(action) {
try {
const response = yield call(upload, payload, [?? anyProgressFunction ??]);
yield put({ type: UPLOADING_SUCCESS, payload: response });
} catch (err) {
yield put({ type: UPLOADING_FAIL, payload: err });
}
}
我想接收进度事件并按saga 放置。我还想捕获 axios 请求的成功(或失败)结果。可能吗?
谢谢。
所以我找到了答案,谢谢 Mateusz Burzyński 的澄清。
我们需要使用 eventChannel,但有点巧妙。
假设我们有api上传文件的功能:
function upload(payload, onProgress) {
const url = '/sources/upload';
const data = new FormData();
data.append('source', payload.file, payload.file.name);
const config = {
onUploadProgress: onProgress,
withCredentials: true
};
return axios.post(url, data, config);
}
在 saga 中我们需要创建 eventChannel 但将 emit 放在外面。
function createUploader(payload) {
let emit;
const chan = eventEmitter(emitter => {
emit = emitter;
return () => {}; // it's necessarily. event channel should
// return unsubscribe function. In our case
// it's empty function
});
const uploadPromise = upload(payload, (event) => {
if (event.loaded.total === 1) {
emit(END);
}
emit(event.loaded.total);
});
return [ uploadPromise, chan ];
}
function* watchOnProgress(chan) {
while (true) {
const data = yield take(chan);
yield put({ type: 'PROGRESS', payload: data });
}
}
function* uploadSource(action) {
const [ uploadPromise, chan ] = createUploader(action.payload);
yield fork(watchOnProgress, chan);
try {
const result = yield call(() => uploadPromise);
put({ type: 'SUCCESS', payload: result });
} catch (err) {
put({ type: 'ERROR', payload: err });
}
}
我个人认为接受的答案非常令人费解,而且我很难实施它。其他 google / SO 搜索都导致了类似类型的答案。如果它对你有用,那很好,但我找到了另一种使用 EventEmitter
的方法,我个人认为它更简单。
在您的代码中的某处创建一个事件发射器:
// emitter.js
import { EventEmitter } from "eventemitter3";
export default new EventEmitter();
在您进行 api 调用的 saga 中,使用此发射器在 onUploadProgress
回调中发出一个事件:
// mysagas.js
import eventEmitter from '../wherever/emitter';
function upload(payload) {
// ...
const config = {
onUploadProgress: (progressEvent) = {
eventEmitter.emit(
"UPLOAD_PROGRESS",
Math.floor(100 * (progressEvent.loaded / progressEvent.total))
);
}
};
return axios.post(url, data, config);
}
然后在你需要这个上传进度号的组件中,你可以在挂载上监听这个事件:
// ProgressComponent.jsx
import eventEmitter from '../wherever/emitter';
const ProgressComponent = () => {
const. [uploadProgress, setUploadProgress] = useState(0);
useEffect(() => {
eventEmitter.on(
"UPLOAD_PROGRESS",
percent => {
// latest percent available here, and will fire every time its updated
// do with it what you need, i.e. update local state, store state, etc
setUploadProgress(percent)
}
);
// stop listening on unmount
return function cleanup() {
eventEmitter.off("UPLOAD_PROGRESS")
}
}, [])
return <SomeLoadingBar value={percent} />
}
这对我有用,因为我的应用程序出于其他原因已经在使用全局 eventEmitter
。我发现这更容易实现,也许其他人也会。