Best practices on imperative custom React hooks that only use effects

Viewed 38

In our code base, I found a variety of custom hooks that do not return any values and whose sole purpose it is to cause side effects based on values selected directly from the Redux store. Take this arbitrary example for illustration:

App.tsx
const App = () => {
    useRedirectIfYellow();

    return <>...</>
}
hooks/useRedirectIfYellow.ts
export const useRedirectIfYellow = () => {
    const navigate = useNavigate();
    const color = useSelector(state => state.currentColor);

    useEffect(() => {
        if (color === "yellow") {
            navigate("/the-yellow-pages");
        }
    });
}

There are cases where 5 of such "imperative hooks" are added to the same component, for purposes like dispatching actions to Redux, initializing external event listeners, and navigating to other pages under certain conditions like above.

To me, this feels like a problem because it hides possible side effects of a component behind those hooks and the behaviors emerge "from the bottom up" instead of the control flow being declared by and visible in the actual controller component. Dependencies should be explicit, not implicit, which is one of the main selling points of constructor dependency injection in classic OOP. Otherwise however, I am unable to properly articulate why this feels like a bad pattern and was hoping to get some input from you on code quality characteristics this pattern violates (or why it's a good idea..?).

I was thinking that one better alternative would be not to rely on app selectors here and instead pass the conditions from the outside, making it clear upon which data the hook operates - or in other cases make them normal functions instead of hooks with all control flow eminating from the controlling component:

hooks/useRedirectIfYellow.ts
export const useRedirectIfYellow = (color: "red" | "green" | "yellow") => {
    const navigate = useNavigate();

    useEffect(() => {
        if (color === "yellow") {
            navigate("/the-yellow-pages");
        }
    });
}

Maybe I also have to rethink my idea of what hooks should represent, i.e. it might be totally okay to use them in an imperative fashion and not just as abstractions for querying or accessing some data.

0 Answers
Related