reactjsreduxreact-routerreselect

How can I pass useParams to mapStateToProps in React with React Router v6?


In my application, I want to pass the parameter that I'm getting from useParams() to the mapStateToProps function which I'm using with the Reselect library.

I can hard code the value instead of passing this parameter and everything works as expected. Also, I can pass useParams().routeName directly to the mapState function and it's working. But in case of using ownProps, it is not working. Here is my code:

const CollectionPage = ({collection}) => {
  let params = useParams();
  let collectionPath = params.routeName;

  return (
    <div className='collection-page'>
    </div>
  )
}

const mapStateToProps = (state, ownProps) => ({
  collection: selectCollection(ownProps.collectionPath)(state)
});

export default connect(mapStateToProps)(CollectionPage);

With this code, the return will be undefined, but when I hard code the value, like the code below, it’s working:

const mapStateToProps = (state) => ({
  collection: selectCollection(''Some Hard Coded Value'')(state)
});

Solution

  • Preferred

    The preferred method would be to use the React hooks directly in the component. Instead of using the connect Higher-Order Component (HOC), use the useSelector hook to select/access the collection state. Refactor the selectCollection selector to return the state and do the filtering in the UI.

    Example:

    import { useSelector } from 'react-redux';
    import { useParams } from 'react-router-dom';
    
    const CollectionPage = () => {
      const { routeName } = useParams();
      const collection = useSelector(state => {
        const collection = selectCollection(state);
        // Logic to return collection filtered by routeName, etc...
        return <UI value here>
      });
    
      return (
        <div className='collection-page'>
          ...
        </div>
      );
    };
    

    Alternative/Legacy

    If you have the need to access path parameters in any mapStateToProps function, and if you are using a lot of other code, for example, then you'll need to create another HOC to access the path parameters and have them injected as props so they are available in the mapStateToProps function.

    Example:

    import { useParams, /* Other hooks */ } from "react-router-dom";
    
    const withRouter = Component => props => {
      const params = useParams();
      // Other hooks, useLocation, useNavigate, etc..
      return <Component {...props} {...{ params, /* other injected props */ }} />;
    };
    
    export default withRouter;
    

    ...

    import { compose } from 'redux';
    import { connect } from 'react-redux';
    import withRouter from '../path/to/withRouter';
    
    const CollectionPage = ({ collection }) => {
      return (
        <div className='collection-page'>
          ...
        </div>
      );
    };
    
    const mapStateToProps = (state, { params: { routeName } = {} }) => ({
      collection: selectCollection(routeName)(state),
    });
    
    export default compose(
      withRouter,              // <-- injects a params prop
      connect(mapStateToProps) // <-- props.params accessible
    )(BlogDetailsPage);