DEV Community

Domantas Jurkus
Domantas Jurkus

Posted on

React: avoid hooks that set default values, use getters with defaults

The context

A colleague of mine helped identify a pattern that has helped avoid using hooks for setting default values.

If you ever found yourself needing to write useSetDefaultSomething(), this article is for you.

The goal

When a customer opens mysite.com/?myParam=foo, I want to load the correct FooComponent. However, if mysite.com is opened with no param is set, I want it to default to FooComponent as well.

In the following component, a hook is used to set some sort of default parameter:

export const MyMainComponent = () => {
  useSetDefaultParameters();

  const { params } = useMyContext();

  return (
    <div className="...">
      {params.myParam === "foo" && <FooComponent />}
      {params.myParam === "bar" && <BarComponent />}
    ....
Enter fullscreen mode Exit fullscreen mode

The hook itself monitors the url params for changes and updates a context whenever myParam changes.

export const useSetDefaultParameters = () => {
  const { myParam } = useGetQueryParam();
  const { setParam } = useMyContext();

  useEffect(() => {
    setParam("myParam", defaults["myParam"]);
  }, [myParam]);
};
Enter fullscreen mode Exit fullscreen mode

The problems

  • UI flicker: since useEffect runs after every render, the UI will initially paint a state where urlParam is undefined before it receives the default value.
  • useSetDefaultParameters is a brittle side-effect: as the app gets built out, there may be different reasons for why we want urlParam to change without affecting the original MyMainComponent.

The solution

Instead of writing your app in a way that requires side-effects, a better option is:

  • Put what you need into a hook, eg useGetMyParam.
  • Make the hook return a default value if urlParam is undefined.
export const useGetMyParam = () => {
  const { myParam } = useGetQueryParam();

  return myParam || defaults["myParam"]
}
Enter fullscreen mode Exit fullscreen mode

Then, our main component can be updated to:

export const MyMainComponent = () => {

  const { myParam } = useGetMyParam();

  return (
    <div className="...">
      {myParam === "foo" && <FooComponent />}
      {myParam === "bar" && <BarComponent />}
    ....
Enter fullscreen mode Exit fullscreen mode

With this, we get rid of the useEffect and our UI renders the correct component from the first time πŸ‘Œ

Top comments (0)