简体   繁体   中英

React: React-Redux: Redux-Thunk: Memory Leak on Unmounted Component

Im going to formulate this question as best I can because this is very confusing. I am using redux for my applications state. and I am using redux-thunk to handle all of my async api calls to the database.

I am using redux-thunk to handle my async POST api call to the database to add data. I have a parent component and a nested component. The parent component contains my data grid which is the AG React Data grid. I am switching from my parent component that contains my data grid, to the component that contains the form I will use to input data to be stored to the database. Once the async call is finished I want to route back to the original view that contains the grid (The grid, and the input form are nested into the Parent component, and Im using react-router to switch between the two views)

Once the async call is dispatched using a thunk, the app calls history.goBack() before the action being dispatched is actually complete, as it should in an async call. The issue is it results in these two errors when routing back to the original view.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Uncaught (in promise) TypeError: rowData.map is not a function

How am I able to make sure that the action is completed inside the thunk code, so that the my component re-renders properly.

Parent Component code

    const store = configureStore();
  const dispatch = useDispatch();

  // selects the proper data from the redux store, this variable is used as row data for 
  // AG Grid

  const events = useSelector((state: AppState) => state.events.list);

  const { path, url } = useRouteMatch();
  const history = useHistory();

  const [showBackBtn, setShowBackBtn = () => showBackBtn] = useState(false);

  const goToAddEvent = () => {
    history.push(`${url}/add-event`);
    setShowBackBtn(true);
  };

  const backBtn = () => {
    history.goBack()
    setShowBackBtn(false);
  }

  const columns = [
    { field: "eventName", headerName: "Event name" },
    { field: "eventDate", headerName: "Event Date" },
    { field: "location", headerName: "Location" },
    { field: "startTime", headerName: "Start Time" },
    { field: "endTime", headerName: "End Time" },
  ];

  //gets initial data, saves to the redux store
  useEffect(() => {
    const doGetEvents = async () => {
      dispatch(getEventsAsyncFromDB());
    };
    doGetEvents();

    //setShowBackBtn(!showBackBtn);
  }, [dispatch, store]);

Code for the form input component

const AddEventComponent = () => {

    const dispatch = useDispatch();
    const history = useHistory();

  const addEvent = (event: any) => {
    event.preventDefault();
    let eventData: Event = {
      eventName: event.target[0].value,
      location: event.target[1].value,
      eventDate: event.target[2].value,
      startTime: event.target[3].value,
      endTime: event.target[4].value,
    };

    dispatch(addEventAsyncToDB(eventData));
    history.goBack();
  };

Redux Thunk code that calls the api to add data to the database

export const addEventAsyncToDB = (event: Event) => {
  return async (dispatch: any) => {
    dispatch(addingEventToDb);
    return await addEvent(event).then((res) => {
        dispatch(addedEventToDBAction(res?.data));
    });
  };
};

And finally here is the code that is making the API call

export const addEvent = async (event: Event) => {
    try{
        const res = await axios.post(`${baseURI}/Events`, event);
        return res;
    }
    catch(e){
        console.log(e);
    }
}

Again the API call is successful, the issue is the history.goBack() line is called before the actual API call is finished processing resulting in the errors above. Please bear with me, this is the best way I can form this question

A simple work-around for this was to add a setTimeout function that would then route the page back to the proper route after adding data to the DB was complete

const addEvent = (event: any) => {
    event.preventDefault();
    let eventData: Event = {
      eventName: event.target[0].value,
      location: event.target[1].value,
      eventDate: event.target[2].value,
      startTime: event.target[3].value,
      endTime: event.target[4].value,
    };

    dispatch(addEventAsyncToDB(eventData));
    setTimeout(() => {
        history.push('/app/events');
    }, 3000);
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM