1

I am using react-router-dom V6, with several different Class components that are connected with various nested within each component. In App.js, I have defined my browser router and routes as follows:

// import omitted for simplicity

function App() {
  return (
    <BrowserRouter>
      <NAVBAR />
      <Routes>
        <Route path="/" element={ <SHOW_OPTIONS />} />
        <Route path="/view_list/:list_id" element={ <SHOW_LIST />} />
        <Route path="/authorize" element={ <AUTHORIZE_ITEM /> } />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Inside of SHOW_OPTIONS, I have a option that brings you to /view_list/:list_id. Inside SHOW_OPTIONS, I need to take the list_id out of the parameter and then query a database for everything with that list_id, then display the list for the end user to browse.

Where I am running into issues is getting that query parameter out of the URL in my class component, since I cannot use any hooks directly from the class.

Here is what my SHOW_LIST class looks like:

export class SHOW_LIST extends Component {
    constructor(props) {
        super(props);
        // Here I want to be able to assign my query
        // param to this.state.list_id
        this.state = {
            list_id: '',
            items: '',
            items_selected: ''
        }
    }

    componentDidMount() {
        getListData(this.state.list_id) // this is == '' right now should be
            .then(res => res.json())              // what was passed
            .then(data => {
                console.log(data);
                this.setState({items: data});
            })
            .catch(err => {
                console.log(err);
            });
    }

    buildList() {
      // where the list is actually
      // built using this.state.items
      return (
        <div>I made it through!</div>
      )
    }

    render () {
        if(this.state.items != null) {
            return (
              {this.buildList()}
            );
        } else {
            return (
                <div id="loading">
                    Loading...
                </div>
            );
        }
    }
}

My link to the SHOW_LIST component looks like this:

<Link to = {{pathname: '/view_list/'+list_id}}></Link>

My initial solution was to do something like what's being done here, but since I need to access the query parameters within my class component, I'm not certain this resolution will work. How to pass params into link using React router v6?

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
backward forward
  • 429
  • 3
  • 17

1 Answers1

1

Once you've followed the directions of the linked answer and created a custom withRouter HOC to inject the route path params you access them via the props object.

Given withRouter HOC:

const withRouter = WrappedComponent => props => {
  const params = useParams(); // <-- these are the path params
  // etc... other react-router-dom v6 hooks

  return (
    <WrappedComponent
      {...props}
      params={params}
      // etc...
    />
  );
};

And route path:

path="/view_list/:list_id"

Decorate the component and access them via this.props.params.list_id. Don't store list_id prop in state as this is actually an anti-pattern in React (storing passed props in local state).

export class SHOW_LIST extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: null,
      items_selected: ''
    }
  }

  componentDidMount() {
    getListData(this.props.params.list_id) // <-- consume from props
      .then(res => res.json())
      .then(data => {
        console.log(data);
        this.setState({items: data});
      })
      .catch(console.error);
  }

  buildList() {
    // where the list is actually
    // built using this.state.items
    return (
      <div>I made it through!</div>
    )
  }

  render () {
    if (this.state.items !== null) {
      return this.buildList();
    } else {
      return (
        <div id="loading">
          Loading...
        </div>
      );
    }
  }
}

Export the decorated SHOW_LIST:

export default withRouter(SHOW_LIST);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    @backwardforward Change from the named import `import { SHOW_LIST } from '.......';` to the default import `import SHOW_LIST from '.......'` since you are now default exporting it. If you don't want to default export the `withRouter(SHOW_LIST)` then rename `SHOW_LIST` in the file and don't export *that* version, and create a new `export const SHOW_LIST = withRouter(RawSHOW_LIST)`. – Drew Reese Nov 13 '21 at 02:02
  • I'm assuming a similar thing is done if I want to pass an entire state as well? Trying to do something similar, but I need to pass more than just a couple values so query parameters aren't working. I am instead using state:{...} in my as a replacement, but changing my HOC to useLocation instead of params I keep getting this.props.location as undefined. Wondering if setting state in isn't setting anything in this.props.location as I expected. – backward forward Nov 15 '21 at 05:44
  • 1
    @backwardforward In the "// etc... other react-router-dom v6 hooks" comment in the `withRouter` HOC, this is where you'd do `const location = useLocation();` and then pass a `location={location}` prop to the wrapped component where the other "// etc..." comment is. Does this make sense? You're not limited in the number of props you want/need to inject. This being said, route state works a little differently now in RRDv6, `state` is a top-level prop now, so you can specify a `state={....}` right in the `Link` component, it's not part of any `to` prop object anymore. – Drew Reese Nov 15 '21 at 05:59