How to use useParams in mapStateToProps?

Viewed 1853

I want specific prop from route params and use it to filter data in redux-store.

Product.js

import React from 'react';
import { useParams } from 'react-router-dom';
import { connect } from 'react-redux';

const Product = (props) => {
    let { slug } = useParams();
    //console.log(props.match)
    return (
        <div>
            <h3>Welcome to <b>{ slug }</b> page</h3>
        </div>
    )
}

const mapStateToProps = ( state, ownProps  ) => {
    // let id = slug;

    return { item: state.items[0]}
}

export default connect(
    mapStateToProps
)(Product);

App.js

function App() {
  return (
    <Router>
      <Navbar/>
      <Switch>
        <Route exact path="/:slug">
          <Product/>
        </Route>
        <Route exact path="/">
          <Home/>
        </Route>
      </Switch>
    </Router>
  );
}

and whatever links that navigate to /slug path are ended up in Product.js, Since Product.js is being nested nowhere else so i couldn't get specific props pass down but route params. From my perspective this is completely wrong but i couldn't figure out a better way to do it.

3 Answers

Since you are using the new version of React and Redux. You can try use Hook to get data from redux store.

Better call useSelector instead. Read more here

import React from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";

const Product = () => {
  let { slug } = useParams();

  const item = useSelector((state) => state.items[slug]);

  console.log(item);

  return (
    <div>
      <h3>
        Welcome to <b>{slug}</b> page
      </h3>
    </div>
  );
};

export default Product;

In your case, you could use the mapDispatchToProps property which is the second argument of connect

Product.js

import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { connect } from 'react-redux';

const Product = (props) => {
    const { slug } = useParams();

    const {
       items, // From mapStateToProps
       filterItems // From mapDispatchToProps
    } = props;
    
    const [filteredItems, setFilteredItems] = useState([]);

    useEffect(() => {
       setFilteredItems(filterItems(items, slug));
    });

    return (
        <div>
            <h3>Welcome to <b>{ slug }</b> page</h3>
            
            <!-- {filteredItems.map(item => { })} -->
        </div>
    )
}

const mapStateToProps = ( state, ownProps  ) => {
    return { items: state.items}
}

const mapDispatchToProps = dispatch => {
   return {
      filterItems: (items, filter) => {
         // TODO: Filter logic goes here...

         return items;
      }
   }
};

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Product);

Another performant solution is to use withRouter

You want to access the state and routing params in one place to select what you want from the state.

One solution to it is using useParams inside the component and while you access the state using connect it's fine.

However, I once found that this solution causes my component to re-render a lot because we don't quietly control how useParams being re-invoked, I prefer using the HOC(higher order component) that react-router-dom offer which is called withRouter (which I found more performant) and here is how to use it

You wrap it around connect

import { withRouter } from "react-router-dom";

...

export const ArticlePageContainer = withRouter(
  connect(mapStateToProps, undefined)(ArticlePageUI)
);

then you can access the slug or any params from inside the props in mapStateToProps function

function mapStateToProps(state, props) {
  const slug = props.match.params.slug;
  return {
    targetArticle: state.items.find((item) => item.slug == slug)
  };
}

Finally, you use that selected piece of data as in your component, where you get it from the props directly now.

function ArticlePageUI(props) {
  return (
    <>
      <p>{"Article Page"}</p>
      <p>{props.targetArticle?.content}</p>
    </>
  );
}

Here's a code sandbox where you can check the implementation yourself https://codesandbox.io/s/stackoverflowhow-to-use-useparams-in-mapstatetoprops-qxxdo?file=/src/article-page.js:87-225

Related