Skip to content

Instantly share code, notes, and snippets.

@warborn
Last active December 20, 2017 22:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save warborn/05031ea49858c85481bb6bd96c0cc605 to your computer and use it in GitHub Desktop.
Save warborn/05031ea49858c85481bb6bd96c0cc605 to your computer and use it in GitHub Desktop.
Duckr Updates

Duckr App

Changes to work with the lastest version of React (v15.6) and React Router (v4.1) when building the Duckr application from the Redux course

You can find the my current version of the app here

Optional

  • I used BrowserRouter as router and used BrowserHistory instead of hashHistory, changes needs to be made in the routes.js file
import { BrowserRouter as Router, Route } from 'react-router-dom'

Changes

The following changes apply to the webpack.config.babel.js file

  • Change webpack resolve root and instead use modules
modules: [path.resolve(__dirname, "app"), "node_modules"]
  • Not ommit the loader suffix when declaring the css loaders
{ test: /\.css$/, loader: 'style-loader!css-loader?sourceMap&modules&localIdentName=[name]__[local]___[hash:base64:5]' }
  • Remove the progress key for the devServer webpack configuration

  • Remove inline key for the devServer webpack configuration as the docs state it's value is true by default

  • Configure the UglifyJS plugin and tell we are using sourcemaps for production

new webpack.optimize.UglifyJsPlugin({ sourceMap: true, minimize: true })

  • Install eslint-plugin-import and eslint-plugin-node to get eslint to work

  • In the routes.js file use MainContainer with a children Route instead of nested Routers and IndexRoute

  <Router>
    <MainContainer>
      <Route exact={true} path='/' component={HomeContainer} />
    </MainContainer>
  </Router>
  • Wrap the redux connected function call inside a withRouter function call in order to work with react router v4
import { withRouter } from 'react-router'
  export default withRouter(connect(
    (state) => ({ isAuthed: state.isAuthed })
  )(MainContainer))

Changed applied to the Route Protection with React Router lesson

You can view the full changes applied in the following commit

  • Use this.context.router.history object to do the 'feed' redirection (Used the 4 way as stated here to stick to the course code)
handleAuth (e) {
  e.preventDefault();

  this.props.fetchAndHandleAuthedUser()
    .then(() => this.context.router.history.replace('feed'))
}
  • In order to replace the onEnter property because is not longer there on react-router v4, i opted for a HOC to make the least amount of changes to the original course code, the idea of using a HOC was taken from here

    1. Create a HOC in a restricted.js file located at /app/helpers/restricted.js, in here we will use the checkIfAuthed helper and pass the redux store as an argument with that we can apply the same rules to make the redirection
    import React, { Component } from 'react'
    import { withRouter } from 'react-router-dom'
    import { checkIfAuthed } from './auth'
    
    export default (BaseComponent, store) => {
      class Restricted extends Component {
        componentWillMount() {
          this.checkAuthentication(this.props);
        }
    
        componentWillReceiveProps(nextProps) {
          if (nextProps.location !== this.props.location) {
            this.checkAuthentication(nextProps);
          }
        }
    
        checkAuthentication(props) {
          const { history } = props;
          const nextPathName = history.location.pathname
          const isAuthed = checkIfAuthed(store)
          if(nextPathName === '/' || nextPathName === '/login') {
            if(isAuthed === true) {
              history.replace({ pathname: '/feed' })
            }
          } else {
            if(isAuthed !== true) {
              history.replace({ pathname: '/login' })
            }
          }
        }
    
        render() {
          return <BaseComponent {...this.props} />;
        }
      }
    
      return withRouter(Restricted);
    }
    1. In the routes.js file instead of setting the onEnter property we will set the component property to the call of the checkAuth function that will call the restricted function and thus give the component it needs
    export default function getRoutes (checkAuth) {
      return (
        <Router>
          <MainContainer>
            <Switch>
              <Route exact={true} path='/' component={checkAuth(HomeContainer)} />
              <Route path='/login' component={checkAuth(AuthenticateContainer)} />
              <Route path='/feed' component={checkAuth(FeedContainer)} />
              <Route path='/logout' component={LogoutContainer} />
            </Switch>
          </MainContainer>
        </Router>
      )
    }
    1. In the /app/index.js file now we need to define the checkAuth function that will accept a component and the redux store and call the restricted function
    import restricted from 'helpers/restricted'
    ...
    function checkAuth (component) {
      return restricted(component, store)
    }
    
    ReactDOM.render(
      <Provider store={store}>
        {getRoutes(checkAuth)}
      </Provider>,
      document.getElementById('app')
    )

  • Use this.context.router.history inside of the onAuthStateChanged callback when authenticating on MainContainer
if (this.props.location.pathname === '/feed') {
  this.context.router.history.replace('feed')
}
  • Add the contentLabel modal property to the ReactModal component
<ReactModal 
  style={modalStyles} 
  isOpen={props.isOpen} 
  onRequestClose={props.closeModal}
  contentLabel='Modal'>
  • Use this.props.match.params.uid instead of this.props.routeParams.uid to get the user's uid from the URL inside the UserContainer component
  componentDidMount () {
    const uid = this.props.match.params.uid
    ...
  }
  • Add the publicPath key to the output object inside the webpack configuration object to load the index_bundle.js file from the root of the application and not from some URL like /duckDetail/index_bundle.js when navigating to the duck's details
output: {
  path: PATHS.build,
  filename: 'index_bundle.js',
  publicPath: '/'
}
  • Place the duckDetail route before the users route, to match first because otherwise an URL like /duckDetail/someId can match the /:uid pattern
<Route path='/duckDetail/:duckId' component={checkAuth(DuckDetailsContainer)}/>
<Route path='/:uid' component={checkAuth(UserContainer)}/>
@Martian2Lee
Copy link

Thank you warborn!

@EstebanMarin
Copy link

I am just starting to review the project. But your initial configurations are working like a charm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment