折腾:
【未解决】Antd Pro中点击提交后禁止再点击按钮
期间,现有的Reactjs的Antd Pro中的相关网络请求的代码是:
this.props.dispatch({ type: 'script/submitScript', payload: params, }).then(() => { this.props.dispatch(routerRedux.push('/script/script-list')); });
->
src/models/script.js
*submitScript({ payload }, { call }) { yield call(createScript, payload); message.success('新建剧本成功'); },
->
src/services/api.js
export async function createScript(params) { return request(`${apiPrefix}/scripts/`, { method: 'POST', body: params, headers: constructHeaders(), }); }
->
src/utils/request.js
export default function request(url, options) { const defaultOptions = { credentials: 'include', }; const newOptions = { ...defaultOptions, ...options }; if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'PATCH') { if (!(newOptions.body instanceof FormData)) { newOptions.headers = { Accept: 'application/json', 'Content-Type': 'application/json; charset=utf-8', ...newOptions.headers, }; newOptions.body = JSON.stringify(newOptions.body); } else { // newOptions.body is FormData newOptions.headers = { Accept: 'application/json', ...newOptions.headers, }; } } return fetch(url, newOptions) // .then(debugResponse) .then(checkStatus) .then(response => { if (newOptions.method === 'DELETE' || response.status === 204) { return response.text(); } if (url.includes('word')) { window.open(url); } return response.json(); }) .catch(e => { console.log(`url=${url} -> error=`, e); const { dispatch } = store; const status = e.name; if (status === 401) { dispatch({ type: 'login/logout', }); return; } if (status === 403) { dispatch(routerRedux.push('/exception/403')); return; } if (status <= 504 && status >= 500) { dispatch(routerRedux.push('/exception/500')); return; } if (status >= 404 && status < 422) { dispatch(routerRedux.push('/exception/404')); } }); }
很明显,此处的http的request,当出错时,最终都被catch中去统一处理掉异常了
从而没有让fetch的调用者直到异常,也就无法自己掌控
比如此处,希望在异常时,去掉之前按钮的disable,允许重新被点击
所以此处,目前能想到的,最完美的情况是:
fetch调用后台api返回后,不论是exception出错,还是正常response返回,都有个callback
而此处的调用接口时,都是dispatch一个字符串
然后跳转到负责的model去处理的
现在正常返回是有callback的
所以要想办法去在现有基础上加个exception的callback
antd pro api 异常 回调函数
this.props.dispatch({ type: '***', payload: '***', callback: () => { // callback function } })
去找找此处是否真的支持callback
才发现是自己去添加的:
*fetch({ payload, callback }, { call, put }) { // request here if (callback) callback() // ** 回调函数 ** })
试试直接传递callback会发生什么
if (this.isEditMode()){ dispatchDict = { type: 'script/updateScript', payload: params, scriptID: this.state.curScriptId, callback: (resp) => { console.log("updateScript callback: resp=", resp) } } } else { dispatchDict = { type: 'script/submitScript', payload: params, callback: (resp) => { console.log("submitScript callback: resp=", resp) } } } console.log("dispatchDict=", dispatchDict) this.props.dispatch(dispatchDict).then(() => { this.props.dispatch(routerRedux.push('/script/script-list')); })
当然没有任何反应,毕竟接口不支持。
去添加支持errorCallback
【总结】
最后采用的是:
虽然不完美,但是凑合可以达到基本的效果:
不论是返回正常数据还是出错,都可以回调函数的方式:
src/routes/Script/ScriptCreateEdit.js
params.dialogs = dialogItemList console.log('params=', params) const okOrErrorCallback = (errOrData) => { console.log("okOrErrorCallback: errOrData=", errOrData) if(!errOrData) { console.log("okOrErrorCallback: Omit response empty") return } this.setState({ isSubmitting: false, isDisableSubmit: false, }) console.log(`okOrErrorCallback: isSubmitting=${this.state.isSubmitting},isDisableSubmit=${this.state.isDisableSubmit}`) if(errOrData instanceof Error) { const errMsg = this.state.curPageTitle + "出错:" + `${errOrData}` console.log(errMsg) message.error(errMsg) } else { const okMsg = this.state.curPageTitle + "成功" console.info(okMsg) message.info(okMsg) this.props.dispatch(routerRedux.push('/script/script-list')) } } let dispatchDict if (this.isEditMode()){ dispatchDict = { type: 'script/updateScript', payload: params, scriptID: this.state.curScriptId, okOrErrorCallback: okOrErrorCallback, } } else { dispatchDict = { type: 'script/submitScript', payload: params, okOrErrorCallback: okOrErrorCallback, } } console.log("dispatchDict=", dispatchDict) this.setState({ isSubmitting: true, isDisableSubmit: true, }) console.log(`Now will submit: isSubmitting=${this.state.isSubmitting},isDisableSubmit=${this.state.isDisableSubmit}`) // const afterSubmitCallback = (resp) => { // console.log("afterSubmitCallback: resp=", resp) // } // this.props.dispatch(dispatchDict).then(afterSubmitCallback()) this.props.dispatch(dispatchDict)
src/models/script.js
*submitScript({ payload, okOrErrorCallback}, { call }) { console.log("submitScript: payload=", payload, ", okOrErrorCallback=", okOrErrorCallback) const resp = yield call(createScript, payload, okOrErrorCallback) console.log("after createScript: resp=", resp) if(okOrErrorCallback) { okOrErrorCallback(resp) } }, *updateScript({ payload, scriptID, okOrErrorCallback}, { call }) { console.log("updateScript: payload=", payload, ", scriptID=", scriptID, ", errorCallback=", okOrErrorCallback) const resp = yield call(updateScript, payload, scriptID); console.log("after updateScript: resp=", resp) if(okOrErrorCallback) { okOrErrorCallback(resp) } },
src/services/api.js
export async function createScript(params, okOrErrorCallback) { console.log("createScript: params=", params, ", okOrErrorCallback=", okOrErrorCallback) return request( `${apiPrefix}/scripts/`, { method: 'POST', body: params, headers: constructHeaders(), }, okOrErrorCallback ) } export async function updateScript(params, scriptID, okOrErrorCallback) { return request( `${apiPrefix}/scripts/${scriptID}/`, { method: 'PUT', body: params, headers: constructHeaders(), }, okOrErrorCallback ) }
src/utils/request.js
import fetch from 'dva/fetch'; import { notification } from 'antd'; import { routerRedux } from 'dva/router'; import store from '../index'; const codeMessage = { 200: '服务器成功返回请求的数据。', 201: '新建或修改数据成功。', 202: '一个请求已经进入后台排队(异步任务)。', 204: '删除数据成功。', 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。', 401: '用户没有权限(令牌、用户名、密码错误)。', 403: '用户得到授权,但是访问是被禁止的。', 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。', 406: '请求的格式不可得。', 410: '请求的资源被永久删除,且不会再得到的。', 422: '当创建一个对象时,发生一个验证错误。', 500: '服务器发生错误,请检查服务器。', 502: '网关错误。', 503: '服务不可用,服务器暂时过载或维护。', 504: '网关超时。', }; function debugResponse(response){ response.json().then( body => { console.log(`debugResponse`) console.log(`typeof body=`, typeof body) console.log(`body=`, body) console.log("JSON.stringify(body)=",JSON.stringify(body)) } ) return response } // function noticeErrorInfo(response, respJson, okOrErrorCallback) { function noticeErrorInfo(response, okOrErrorCallback) { console.log("noticeErrorInfo: response=", response, ", okOrErrorCallback=", okOrErrorCallback) console.log(`typeof response=`, typeof response) console.log("response.status=", response.status) console.log("response.url=", response.url) response.json().then( respJson => { console.log("response.json().then: respJson=", respJson) console.log(`typeof respJson=`, typeof respJson) console.log("JSON.stringify(respJson)=",JSON.stringify(respJson)) let respMessage if ("message" in respJson) { respMessage = respJson.message console.log(`respMessage=`, respMessage) } else { console.log("message not in response body") } const errorText = respMessage || codeMessage[response.status] || response.statusText console.log("errorText=", errorText) notification.error({ message: errorText, description: `请求 ${response.url} 出错 ${response.status}`, }) const reqUrlError = new Error(errorText) reqUrlError.name = response.status reqUrlError.response = response console.log("reqUrlError=", reqUrlError) // throw reqUrlError // return reqUrlError if(okOrErrorCallback) { okOrErrorCallback(reqUrlError) // console.log("return reqUrlError=", reqUrlError) // return reqUrlError } }) } /** * Requests a URL, returning a promise. * * @param {string} url The URL we want to request * @param {object} [options] The options we want to pass to "fetch" * @return {object} An object containing either "data" or "err" */ export default function request(url, options, okOrErrorCallback) { console.log(`request: url=${url}, options=`, options, ", okOrErrorCallback=", okOrErrorCallback) const defaultOptions = { credentials: 'include', }; const newOptions = { ...defaultOptions, ...options }; if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'PATCH') { if (!(newOptions.body instanceof FormData)) { newOptions.headers = { Accept: 'application/json', 'Content-Type': 'application/json; charset=utf-8', ...newOptions.headers, }; newOptions.body = JSON.stringify(newOptions.body); } else { // newOptions.body is FormData newOptions.headers = { Accept: 'application/json', ...newOptions.headers, }; } } return fetch(url, newOptions) // .then(debugResponse) .then(response => { console.log("fetch response=", response, ",newOptions=", newOptions) if (newOptions.method === 'DELETE' || response.status === 204) { return response.text() } if (response.status >= 200 && response.status < 300) { console.log("ok -> response.status=", response.status) return response.json() } // response.json().then( // respJson => { // noticeErrorInfo(response, respJson, okOrErrorCallback) // } // ) return noticeErrorInfo(response, okOrErrorCallback) }) .catch(err => { console.error(`catch: url=${url} -> error=`, err) const errorText = `解析请求${url}的返回时出错:${err}` console.log("errorText=", errorText) const parseRespError = new Error(errorText) console.log("parseRespError=", parseRespError) if(okOrErrorCallback) { console.log("return parseRespError=", parseRespError) okOrErrorCallback(parseRespError) } // if (okOrErrorCallback) { // okOrErrorCallback(err) // } else { // const { dispatch } = store; // const status = err.name // if (status === 401) { // dispatch({ // type: 'login/logout', // }); // return; // } // if (status === 403) { // dispatch(routerRedux.push('/exception/403')); // return; // } // if (status <= 504 && status >= 500) { // dispatch(routerRedux.push('/exception/500')); // return; // } // if (status >= 404 && status < 422) { // dispatch(routerRedux.push('/exception/404')); // } // } }); }
基本上实现了:
如果是出错,则不论是request的url的error,还是response的解析为text或json等的error,都会调用回调函数okOrErrorCallback,然后可以在okOrErrorCallback中写正常和出错的处理的逻辑:
(1)出错情况:
此处去删除本地local storage中token:
模拟没有登录,去调试401没权限的效果是:
以及:
断开localhost的server,模拟网络断开的效果:
(2)正常情况:
转载请注明:在路上 » 【已解决】reactjs的Antd Pro中如何让网络异常出错时也有callback返回可以自己控制异常