Tachyon80
Tachyon80

Reputation: 157

React-Router Routes Behaving Strangely

I'm trying to run a book example that features a movies store. I've got the page to pull up and look right. The trouble is, when I click on a movie image nothing happens (the react-router is supposed to show an info box about the selected movie). My start URL is: http://localhost/~johndoe/react-dir/test-webpack/

When I click on a movie image nothing happens and the address bar changes to: http://localhost/movies/1

The Chrome dev tools, console says: browser.js:49 Warning: [react-router] Location "/movies/1" did not match any routes

My routes.js file is:

const React = require('react');
const {Router, Route, IndexRoute, browserHistory} = require('react-router');
const App = require('components/app/app.js');
const Movies = require('components/movies/movies.js');
const Movie = require('components/movie/movie.js');
module.exports = (
<Router history={browserHistory}>
    <Route path={"/~johndoe/react-dir/test-webpack/"} component={App}>
        <IndexRoute component={Movies}/>
        <Route path={"movies"} component={Movies}>
            <Route path={":id"} component={Movie}/>
        </Route>
    </Route>
</Router>
);

Can anyone suggest what might be going wrong? I'm not sure what to look for to fix this. Any help would be appreciated.

Upvotes: 0

Views: 143

Answers (1)

Matt Carlotta
Matt Carlotta

Reputation: 19762

Alright took a bit to deconstruct what was going on, but here's a working version.

What's was going wrong:

  • You can't map over an undefined object or array. You need conditional rendering to check if the item is empty -- I used lodash's isEmtpy as it makes life simpler over: array && array.length > 0 for arrays and/or Object.entries(obj).length === 0 && obj.constructor === Object for objects. Without conditional rendering, when you clicked on the link, you were essentially breaking your app.

What I changed:

  • Added conditional rendering using lodashes isEmpty. If the movies or movie value isn't present yet, it'll show a Loading... or null element.
  • Utilized componentDidMount and componentDidUpdate lifecycles. Since React 16+, componentWillMount and componentWillUpdate have been deprecated and aren't recommended.
  • Moved to Babel ES6 import and export statements -- can't think of a reason to use module.exports over import and export, especially if you're using babel to transpile your code. Now-a-days, you'll more than likely find the two bundled in a boilerplate. That said, you're more than welcome to make your life harder and not use babel. :)
  • I don't particularly like redux-actions as it obscures what's going on with redux and its store. So I separated everything out into actions, reducers, and types.

I find it easier to understand the flow (the circle of life):

  • Within a connected component, a user clicks an element (like a button), which triggers the action.
  • The action returns a type and a payload to the reducer.
  • The reducer matches against the type and shallow merges the payload into the reducer state.
  • The component that is connected to redux accepts the new reducer state as props.

Issues you'll have with your current setup:

  • Using strings as imports will not work when your application is bundled. They'll resolve to directories that no longer exist. It's recommended that you either serve the images (like a CDN -- check out the JSON movie.cover in the codesandbox for examples) or import them directly into the file that requires them.
  • If a user tries to go directly to movies/:id, it'll break the application as movies wouldn't be currently defined in your redux state. As such, instead of using routing to showcase the details, I'd recommend using React's local state to pop out and hide the detail view based upon which movie was clicked.

Recommended:

  • Your dependencies are fairly outdated. Unless absolutely necessary, I'd suggest upgrading react and react-dom from 15.2 to 16.x. and upgrading from react-router 2.6 to react-router-dom or at the very least to react-router v3+.
  • If you're looking for a more up-to-date boilerplate, I'd recommend my Webpack-React-Boilerplate.

Working example: https://codesandbox.io/s/nk715xpqv4 (the codesandbox CRA template doesn't support CSS modules, so I had to import the styles and use the className as a string, but that doesn't mean you need to for your project).

Upvotes: 1

Related Questions