Search code examples
javascriptreactjsreduxreact-reduxreact-router-dom

Why props is an empty for me in StreamEdit component?


When I want to access props that are passed through Route in my App.js component, in StreamEdit component it's empty.

It must have match.params.id.

App.js

import { Route, Routes } from 'react-router-dom';
import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import './App.css';
//,BrowserRouter as Router

const App = () => {
  return (
    <div id='app_div'>
      <HistoryRouter history={history}>
        <Header />
        <Routes>
          <Route path='/' exact Component={StreamList} />
          <Route path='/streams/new' Component={StreamCreate} />
          <Route path='/streams/edit/:id' Component={StreamEdit}  />
          <Route path='/streams/delete' Component={StreamDelete} />
          <Route path='/streams/show' Component={StreamShow} />
        </Routes>
      </HistoryRouter>
    </div>
  );
};

export default App;

StreamEdit

import React from "react";  
import { connect } from "react-redux";
import { fetchStream } from '../../actions';

class StreamEdit extends React.Component{
  componentDidMount() {
    this.props.fetchStream(this.props.match.params.id);
  }
}

const mapStateToProps = (state, ownProps) => {
  console.log(ownProps)
  return { stream: state.streams };
}

export default connect(mapStateToProps, { fetchStream })(StreamEdit);

I expect to achieve some data from this.props.match.params.id.


Solution

  • React-Router-DOM routes don't have any props to pass. Use the useParams hook to access the id route path parameter. This means converting the StreamEdit class component to a function component.

    import React from "react";
    import { useParams } from "react-router-dom";
    import { connect } from "react-redux";
    import { fetchStream } from '../../actions';
    
    const StreamEdit = ({ fetchStream, stream }) => {
      const { id } = useParams();
    
      React.useEffect(() => {
        fetchStream(id);
      }, [fetchStream, id]);
        
      ...
    }
    
    const mapStateToProps = (state, ownProps) => {
      console.log(ownProps)
      return {
        stream: state.streams
      };
    }
    
    export default connect(mapStateToProps, { fetchStream })(StreamEdit);
    

    React class component have been all but deprecated since 2019 when React hooks were released, and modern Redux no longer uses the connect Higher Order Component, so a modern implementation would also use React-Redux's useDispatch and useSelector hooks.

    Example:

    import React from "react";
    import { useParams } from "react-router-dom";
    import { useDispatch, useSelector } from "react-redux";
    import { fetchStream } from '../../actions';
    
    const StreamEdit = () => {
      const dispatch = useDispatch();
      const streams = useSelector(state => state.streams);
    
      const { id } = useParams();
    
      React.useEffect(() => {
        dispatch(fetchStream(id));
      }, [dispatch, id]);
        
      ...
    }
    
    export default StreamEdit;
    

    Additionally, the Route component's Component prop is only helpful/useful if you are using a Data router, i.e. one created via one of the create-X-Router utilities and rendered in a RouterProvider. Use the element prop instead, and the components passed as JSX. This should also help highlight why and how no props are passed to the routed components.

    const App = () => {
      return (
        <div id='app_div'>
          <HistoryRouter history={history}>
            <Header />
            <Routes>
              <Route path='/' element={<StreamList />} />
              <Route path='/streams/new' element={<StreamCreate />} />
              <Route path='/streams/edit/:id' element={<StreamEdit />}  />
              <Route path='/streams/delete' element={<StreamDelete />} />
              <Route path='/streams/show' element={<StreamShow />} />
            </Routes>
          </HistoryRouter>
        </div>
      );
    };