0

How to save a function instead of a value in RTK so that i can save a function reference in a component and call it in another component in React?

I just want to reset timer from another component. I don't know what to do so i made a state in react for that function reference

HOME COMPONENT

import React, { useEffect } from "react";
import { Outlet } from "react-router-dom";
import { useSelector,useDispatch } from "react-redux";
import Sidebar from "../components/Sidebar/Sidebar";
import { useIdleTimer } from "react-idle-timer";
import { userLogout } from "../utils/utils";
import LoadingAnimation from "../components/LoadingAnimation/LoadingAnimation";
import { setLogoutTimerReset } from "../store/slicers/logoutSlice";

const Home = () => {
    const { user } = useSelector((state) => state.auth);
    const dispatch = useDispatch();
    
    const onIdle = () => {
        userLogout();
    };

    const { reset } = useIdleTimer({
        onIdle,
        timeout: 60 * 30 * 1000, //60 minute idle timeout
        // timeout: 30000_000,
    });

    useEffect(() => {
        dispatch(setLogoutTimerReset(reset));

        return () => {};
    }, []);

    return user ? (
        <section className="home-sidebar-flex">
            <Sidebar />

            <main className="map-main-parent--container">
                <Outlet />
            </main>
        </section>
    ) : (
        <LoadingAnimation />
    );
};

export default Home;

SOME OTHER COMPONENT

import React from 'react'
import { useSelector } from "react-redux";

function App() {
  const { resetLogoutTimer } = useSelector((state) => state.logoutState);

  return (
    <div onClick={resetLogoutTimer}>Resetter</div>
  )
}

export default App

SLICE CODE

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    resetLogoutTimer: () => {},
};

export const logoutSlice = createSlice({
    name: "logout",
    initialState,
    reducers: {
        setLogoutTimerReset: (state, payload) => {
            state.resetLogoutTimer = payload;
        },
    },
});

export const {  setLogoutTimerReset } = logoutSlice.actions;

export default logoutSlice.reducer;

Reset idleTime from another component I am using react-idle-timer

2 Answers2

0

How to save a function instead of a value in RTK so that i can save a function reference in a component and call it in another component in React?

Personally I wouldn't recommend it since this is what for redux or context were created. You can move your logic there and just call it anywhere among your application.

However if you really insist on keeping your solution, I believe you are missing the dispatch.

const dispatch = useDispatch();

useEffect(() => {
    dispatch(setLogoutTimerReset(reset));

    return () => {};
}, []);
kind user
  • 40,029
  • 7
  • 67
  • 77
  • then what should i do i am getting this error A non-serializable value was detected in the state, in the path: `logoutState.resetLogoutTimer.payload`. Value: t => A.current ? !1 : (x(), V(), ue.current = v(), F.current += v() - w.current, te.current += v() - w.current, F.current = 0, h.current = !1, E.current = !1, H.current = !1, S.current = 0, N.current… Take a look at the reducer(s) handling this action type: baseApi/subscriptions/internal_probeSubscription. – Mubasher Mukhtar Aug 25 '23 at 14:51
  • its still not working i am getting same error I have edited my question. i forgot to share slice code – Mubasher Mukhtar Aug 25 '23 at 14:55
  • @MubasherMukhtar Just disable the `serializableCheck`. https://stackoverflow.com/questions/61704805/getting-an-error-a-non-serializable-value-was-detected-in-the-state-when-using – kind user Aug 25 '23 at 16:11
  • i did that but that didn't work @Drew was correct i saved the whole object. but that worked only for Home's child component. If i call that function in Home's parent that doesn't work – Mubasher Mukhtar Aug 28 '23 at 07:08
0

You are storing the entire action object in the state instead of just the reset function, a.k.a. the action.payload. In other words, resetLogoutTimer in the other component isn't the reset function, resetLogoutTimer.payload is.

Update the reducer function from

setLogoutTimerReset: (state, payload) => { // <-- payload is action object!
  state.resetLogoutTimer = payload;
},

to

setLogoutTimerReset: (state, action) => {
  state.resetLogoutTimer = action.payload;
},

Since it's preferable to store JSON serializable data in a Redux store you may also need to add/update the Serializability Middleware to handle the non-serializable function you are storing in the store.

Example:

const store = configureStore({
  ...,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [setLogoutTimerReset],
        ignoredPaths: ["resetLogoutTimer"],
      },
    }),
});

This all said, it's actually preferable to just lift the useIdleTimer hook and reset function higher in the ReactTree to a common ancestor and pass down as a props or via a React Context to the descendent components that need it.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • You are correct i saved the whole object. It worked only for Home's child component. If i call that function in Home's parent that doesn't work – Mubasher Mukhtar Aug 28 '23 at 07:09
  • @MubasherMukhtar Sorry, what function are you saying you are calling in the components? Are you referring to the dispatch of the `setLogoutTimerReset` action? Or trying to call the "reset" function that was stored in state? If you still need help with this then perhaps you could try standing up a ***running*** [codesandbox](https://codesandbox.io/) demo that we could inspect live and debug, which would be beneficial. – Drew Reese Aug 28 '23 at 07:12