Search code examples
reactjsreduxreact-reduxreact-hooks

Reusing a variable that is based on multiple redux props without duplication of code


I want to set a property based on multiple values from my redux store. (If it makes any difference I use Rematch to handle my global store)

In my case, a boolean property called showSidebar which is only true if several other conditions are met - as a simplified example: loggedIn === true and userProjects.length > 0.

The logic is fine, e.g.

const showSidebar = loggedIn && userProjects.length > 0;

...but I use this in lots of components, and every time I have to import all of the dependent properties and include the conditional logic.

How can I avoid needless duplication of logic and imported props?

I was thinking the best way may be to write my own useShowSidebar hook, which would look something like the following:

import { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';

const useShowSidebar = () => {
  const loggedIn = useSelector((models) => models.model.loggedIn);
  const userProjects = useSelector((models) => models.model.userProjects);
  const [showSidebar, setShowSidebar] = useState(false);

  useEffect(() => {
    setShowSidebar(loggedIn && userProjects.length > 0);
  }, [loggedIn, userProjects]);

  return showSidebar;
};

export default useShowSidebar;

Would this be a sensible and efficient way to achieve this without any issues? Or is there a better way? I assume this must be a common scenario.

I feel like there should be a way to have the property showSidebar in the global store itself as a property that is dependent upon other properties, and just import this one property into my components (using either mapStateToProps or the more modern useSelector hook, but I can't figure out if this is possible.


Solution

  • Your use of useState causes an unneccessary rerender. Just write it all as one selector.

    const useShowSidebar = () => {
      const showSidebar = useSelector((state) => 
        state.model.loggedIn && state.model.userProjects.length > 0
      );
    
      return showSidebar;
    };
    

    Or move it into a selector function and use that in your components:

    const selectShowSidebar = (state) => 
        state.model.loggedIn && state.model.userProjects.length > 0;
    
    // in your component:
    const showSidebar = useSelector(selectShowSidebar)