React Native函数式组件内存泄漏问题求助:触发“无法在已卸载组件上更新状态”警告
Hey there! That warning pops up because your async API call for the job detail finishes after you've navigated away from the JobDetailScreen (so the component is unmounted), and then the saga tries to update the Redux state tied to that unmounted component. Let's fix this properly by canceling both the saga task and the underlying API request when the component unmounts.
Step 1: Add a Cancel Action Type
First, let's add a new action type to handle canceling the single job request:
// jobdetail.type.js export const GET_SINGLE_JOB = "GET_SINGLE_JOB"; export const CANCEL_SINGLE_JOB = "CANCEL_SINGLE_JOB"; // Add this line
Step 2: Create the Cancel Action
Add the corresponding action creator:
// jobdetail.action.js import * as types from "./jobdetail.type"; export const get_single_jobs = (data) => ({ type: types.GET_SINGLE_JOB, payload: data, }); // Add this cancel action export const cancel_single_job = () => ({ type: types.CANCEL_SINGLE_JOB, });
Step 3: Update the Saga to Support Cancellation
Modify your saga to listen for the cancel action, cancel the ongoing API request, and stop any state updates:
// jobdetail.saga.js import { takeLatest, call, put, take, race, CancelledError } from "redux-saga/effects"; import axios from 'axios'; // Import axios for cancel token import SinglejobsServices from './jobdetail.services'; import * as types from './jobdetail.type'; import * as loadertypes from '../loader/loader.type'; function* get_single_jobs_service(payload) { // Create an axios cancel token to abort the API request if needed const cancelSource = axios.CancelToken.source(); try { yield put({ type: loadertypes.LOADERSTART}); // Race the API call against the cancel action const { response, cancelSignal } = yield race({ response: call(SinglejobsServices.list, payload, cancelSource), cancelSignal: take(types.CANCEL_SINGLE_JOB) }); // If cancel was triggered, abort the request and exit early if (cancelSignal) { cancelSource.cancel("Request cancelled: component unmounted"); yield put({ type: loadertypes.LOADERSTOP}); return; } // Handle the API response as before if(response.status == false){ yield put({ type: types.SINGLE_JOBS_ERROR, response }); yield put({ type: loadertypes.LOADERSTOP}); }else{ yield put({ type: types.SET_SINGLE_JOB, response }); yield put({ type: loadertypes.LOADERSTOP}); } } catch(error) { // Ignore cancellation errors if (error instanceof CancelledError || axios.isCancel(error)) { console.log("Single job request cancelled"); yield put({ type: loadertypes.LOADERSTOP}); return; } // Handle other errors normally let response = error.response.data; yield put({ type: types.SINGLE_JOBS_ERROR, response }); yield put({ type: loadertypes.LOADERSTOP}); } } export default function* watchSinglejobs() { // Use takeLatest to automatically cancel previous tasks if a new request comes in yield takeLatest(types.GET_SINGLE_JOB, get_single_jobs_service); }
Step 4: Update the Service to Accept Cancel Token
Modify your API service to use the cancel token:
// Jobdetail.services.js import axios from "axios"; import {API_URL} from '../../config/constant'; function services(){ // Add cancelSource as a parameter const list = (request, cancelSource) => { let req = request.payload; const config = { method: 'get', url: API_URL + '/jobs/details/'+req.job_id, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${req.user.userToken}` }, cancelToken: cancelSource.token // Attach the cancel token } return axios(config) .then(response => { let result = response.data; return result; }) } return { list, }; } const SinglejobsServices = services(); export default SinglejobsServices;
Step 5: Add Cleanup to the Component
Finally, update your JobDetailScreen to dispatch the cancel action when the component unmounts:
// JobDetailScreen.js import { useDispatch } from 'react-redux'; import { get_single_jobs, cancel_single_job } from './jobdetail.action'; // Import the cancel action // ... inside your component const dispatch = useDispatch(); useEffect(() => { let data = {} data.user = login data.job_id = route.params.job_id dispatch(get_single_jobs(data)); // Cleanup function: runs when component unmounts return () => { dispatch(cancel_single_job()); }; },[dispatch, login, route.params.job_id]); // Add dependencies to the effect
Why This Works
- When you navigate away from the screen, the component's cleanup function dispatches the
CANCEL_SINGLE_JOBaction. - The saga catches this action via the
raceeffect, cancels the ongoing API request using axios's cancel token, and exits without updating the Redux state. - This prevents any state updates from happening after the component has unmounted, eliminating the memory leak warning.
内容的提问来源于stack exchange,提问作者Nikul Panchal

