我的控制台出现此错误: “无法在已卸载的组件上执行React状态更新。这是无操作,但表明您的应用程序中存在内存泄漏。要修复,取消useEffect清理功能中的所有订阅和异步任务。”
这是因为我的组件正在调用带有内部异步调用的customHook ...,但是在调用返回我需要的值之前,组件本身已卸载。
const ResolveData = ({ dispatch }) => {
const [loading, error] = setPermission(dispatch);
useEffect(() => {
return () => {
console.log("canceled");
};
}, []);
return ...;
};
我的钩子“ setPermission”调度了一个值,该值使组件再次重新加载(useReducer位于父目录中)。 重新加载组件时,它将再次调用customHook,但是该组件已卸载。 至少,这就是我对问题的了解。
此组件使用customHook返回的变量将我重定向到另一个组件。
这是我的customHook
export default dispatch => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
const getUserPermission = async () => {
try {
setLoading(true);
const response = await requests.getData("/url"); //my URL
dispatch({
type: "dispatch_type",
payload: response.data.role
});
} catch (err) {
if (axios.isCancel(err)) {
console.log(err);
}
setError(true);
} finally {
setLoading(false);
}
};
if (getToken()) {
getUserPermission();
}
return () => {
const cancelToken = createCancelToken();
cancelToken.cancel("canceled");
};
}, [dispatch]);
return [loading, error];
};
在卸载组件时如何禁用customHook调用的任何帮助吗?
答案 0 :(得分:1)
这是我对useApi
的实现的样子:
// libs
import axios from 'axios';
import { useReducer, useRef } from 'react';
export const actionTypes = {
SET_LOADING: 'SET_LOADING',
SET_DATA: 'SET_DATA',
SET_ERROR: 'SET_ERROR',
};
export const fetchData = async (dispatch, cancelToken, action, params) => {
dispatch({ type: actionTypes.SET_LOADING });
return action(params, cancelToken)
.then(response => response.data)
.then(payload => {
dispatch({ type: actionTypes.SET_DATA, payload });
return payload;
})
.catch(error => {
if (!axios.isCancel(error)) {
dispatch({ type: actionTypes.SET_ERROR, error });
throw error;
}
});
};
const initialState = { isLoading: false, payload: {}, error: null };
export const reducer = (state, action) => {
const { type, payload, error } = action;
switch (type) {
case actionTypes.SET_LOADING:
return { ...state, isLoading: true, error: null };
case actionTypes.SET_DATA:
return { ...state, isLoading: false, error: null, payload };
case actionTypes.SET_ERROR:
return { ...state, isLoading: false, error };
default:
return state;
}
};
/**
* Reusable hook to make api calls using a function (action).
* It handles cancellation of previous requests automatically.
*
* @typedef State
* @type {object}
* @property {object} payload - Api response.
* @property {boolean} isLoading - status of Api call.
* @property {object} error - error object in case of failed call.
*
* @typedef ExecuteAction
* @type {function}
* @param {object} params - params to pass to action
* @returns {Promise} - resolves with payload
*
* @typedef useApi
* @param {function} action
* @returns [State, ExecuteAction, cleanupAction]
*/
const useApi = action => {
const [state, dispatch] = useReducer(reducer, initialState);
const axiosSource = useRef(null);
const cleanupAction = () => {
if (axiosSource.current) {
axiosSource.current.cancel('Cleaned up previous request.');
}
};
const executeAction = (params = {}) => {
cleanupAction();
axiosSource.current = axios.CancelToken.source();
return fetchData(dispatch, axiosSource.current.token, action, params);
};
if (!action || typeof action !== 'function') {
throw Error('Missing action || type of action is not function.');
}
return [state, executeAction, cleanupAction];
};
export default useApi;
您可以在组件中使用它,例如:
const getData = (params, cancelToken) => axios.post('some url', params, { cancelToken });
const SomeComponent = () => {
const [state, fetchData, cleanup] = useApi(getData);
const { isLoading, error, payload } = state;
useEffect(() => {
fetchData({ key: 'some params to pass to getData action' });
return cleanup;
})
}
您可以根据需要调整useApi
。