connected-react-router

repository·master·Indexed 26 days ago

https://github.com/supasate/connected-react-router

A Redux binding for React Router v4 and v5 (version 6.9.3) that synchronizes router state with the Redux store. It enables uni-directional data flow, time-travel debugging, and the ability to dispatch navigation actions—such as push, replace, and go—from Redux middleware using routerMiddleware. It provides the ConnectedRouter component to integrate routing into React applications and supports Immutable.js and React Native.

Tokens
4.6K
Snippets
13
Records
26
Agent score
89%

What's inside connected-react-router

  1. Use custom context with react-redux

    master

    If you are using a custom context with react-redux v6.0.0+, you must pass that same context to the <ConnectedRouter> component as a prop.

    const customContext = React.createContext(null)
    
    ReactDOM.render(
      <Provider store={store} context={customContext}>
        <ConnectedRouter history={history} context={customContext}>
          ...
        </ConnectedRouter>
      </Provider>
    )
  2. Integrate ConnectedRouter into your React application

    master

    To connect your routing to Redux:

    1. Wrap your React Router v4/v5 routing with the ConnectedRouter component.
    2. Pass the same history object used in your reducer and middleware to the history prop of ConnectedRouter.
    3. Ensure ConnectedRouter is a child of the react-redux Provider.
    4. Warning: Remove any usage of BrowserRouter or NativeRouter, as they will conflict with the state synchronization.
    // index.js
    import { Provider } from 'react-redux'
    import { Route, Switch } from 'react-router' // react-router v4/v5
    import { ConnectedRouter } from 'connected-react-router'
    import configureStore, { history } from './configureStore'
    
    const store = configureStore(/* provide initial state if any */)
    
    ReactDOM.render(
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <Switch>
            <Route exact path="/" render={() => (<div>Match</div>)} />
            <Route render={() => (<div>Miss</div>)} />
          </Switch>
        </ConnectedRouter>
      </Provider>,
      document.getElementById('react-root')
    )
  3. Migrate from v4 to v5/v6

    master

    Migration involves three main changes:

    1. Reducers: Change your root reducer from a static object to a function that accepts history. Use connectRouter(history) for the router key.
    2. Store Configuration: In createStore, call your new root reducer function with history instead of wrapping the old reducer with connectRouter.
    3. Hot Reloading: Update store.replaceReducer to use the new function-based root reducer creation.
    // reducers.js
    - export default combineReducers({
    + export default (history) => combineReducers({
    +   router: connectRouter(history),
        ...
      })
    
    // configureStore.js
    - import { connectRouter, routerMiddleware } from 'connected-react-router'
    - import rootReducer from './reducers'
    + import { routerMiddleware } from 'connected-react-router'
    + import createRootReducer from './reducers'
    
    - const store = createStore(
    -   connectRouter(history)(rootReducer),
    + const store = createStore(
    +   createRootReducer(history),
        initialState,
        ...
      )
  4. Use connected-react-router with React Native

    master

    Since React Native does not support the HTML5 history API, use createMemoryHistory from the history package to manage routing state in Redux.

    import { createMemoryHistory } from 'history'
    
    const history = createMemoryHistory()
    
    ReactDOM.render(
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <Route path="/" component={myComponent} exact={true} />
        </ConnectedRouter>
      </Provider>
    )
  5. Support Immutable.js

    master

    To use connected-react-router with Immutable.js, follow these steps:

    1. Use combineReducers from redux-immutable.
    2. Import connectRouter from connected-react-router/immutable and add it to your root reducer.
    3. Import ConnectedRouter and routerMiddleware from connected-react-router/immutable instead of the standard package.
    4. Ensure your rootReducer is a function that accepts history and returns the reducer.
    5. (Optional) Initialize your state with Immutable.Map().
    import { combineReducers } from 'redux-immutable'
    import { connectRouter } from 'connected-react-router/immutable'
    
    const rootReducer = (history) => combineReducers({
      router: connectRouter(history),
      ...
    })
    
    import { ConnectedRouter, routerMiddleware } from 'connected-react-router/immutable'
    
    const store = createStore(
      rootReducer(history),
      initialState,
      ...
    )
  6. Configure the router reducer

    master

    In your root reducer file, create a function that accepts history as an argument and returns the root reducer. Use connectRouter(history) to add the router reducer.

    Important: The key in the combineReducers object MUST be named router.

    // reducers.js
    import { combineReducers } from 'redux'
    import { connectRouter } from 'connected-react-router'
    
    const createRootReducer = (history) => combineReducers({
      router: connectRouter(history),
      // ... rest of your reducers
    })
    export default createRootReducer
  7. Configure the Redux store with routerMiddleware

    master

    When setting up your Redux store:

    1. Create a history object (e.g., using createBrowserHistory from the history package).
    2. Pass this history object to your root reducer creator.
    3. Use routerMiddleware(history) in your middleware configuration to enable dispatching history actions (like push('/path')) from Redux actions.
    // configureStore.js
    import { createBrowserHistory } from 'history'
    import { applyMiddleware, compose, createStore } from 'redux'
    import { routerMiddleware } from 'connected-react-router'
    import createRootReducer from './reducers'
    
    export const history = createBrowserHistory()
    
    export default function configureStore(preloadedState) {
      const store = createStore(
        createRootReducer(history), // root reducer with router state
        preloadedState,
        compose(
          applyMiddleware(
            routerMiddleware(history), // for dispatching history actions
            // ... other middlewares ...
          ),
        ),
      )
    
      return store
    }
  8. Navigate using Redux actions

    master

    You can trigger navigation from various parts of your application using the push action creator from connected-react-router.

    • Directly with store.dispatch: Use store.dispatch(push('/path')).
    • With react-redux: Connect the push action to your component using connect and call it from event handlers.
    • In Redux Thunk: Dispatch the push action within a thunk function.
    • In Redux Saga: Use the put effect to dispatch the push action.
    // with store.dispatch
    import { push } from 'connected-react-router'
    store.dispatch(push('/path/to/somewhere'))
    
    // with react-redux
    import { push } from 'connected-react-router'
    export default connect(null, { push })(Component);
    
    // in redux thunk
    import { push } from 'connected-react-router'
    export const login = (username, password) => (dispatch) => {
      dispatch(push('/home'))
    }
    
    // in redux saga
    import { push } from 'connected-react-router'
    import { put } from 'redux-saga/effects'
    export function* login(username, password) {
      yield put(push('/home'))
    }
  9. Access the current browser location (URL) from Redux state

    master

    The current location (including pathname, search, and hash) is stored in the router state. You can access these properties by mapping them from state.router.location using react-redux's connect function.

    import { connect } from 'react-redux'
    
    const mapStateToProps = state => ({
      pathname: state.router.location.pathname,
      search: state.router.location.search,
      hash: state.router.location.hash,
    })
    
    export default connect(mapStateToProps)(Child)
  10. Handle React Redux Context when using npm link

    master

    If you are testing example apps using npm link or yarn link, you must explicitly provide the same ReactReduxContext to both the Provider and the ConnectedRouter to prevent them from picking up different contexts from different node_modules folders.

    // index.js
    import { Provider, ReactReduxContext } from 'react-redux'
    
    <Provider store={store} context={ReactReduxContext}>
      <App history={history} context={ReactReduxContext} />
    </Provider>
    
    // App.js
    const App = ({ history, context }) => {
      return (
        <ConnectedRouter history={history} context={context}>
          { routes }
        </ConnectedRouter>
      )
    }
  11. Disable initial location change

    master

    To prevent the LOCATION_CHANGE action from being dispatched during the initial load, pass the noInitialPop prop to the <ConnectedRouter> component.

    <ConnectedRouter history={history} noInitialPop>
      <Route path="/" component={myComponent} />
    </ConnectedRouter>