React-Router v4. How to append a parameter to URL? TypeError: history.push is not a function

Viewed 8112

I'm using i18-next library to switch languages in my app. So it's done without reloading a page. The language switch is done via:

render() {

    const toggle = lng => i18n.changeLanguage(lng);   
    return (  
       <a href="javascript:void(0)" onClick={()=>{ toggle();}}></a>
    )

I'd make such functionality: once the language is switched add to a URL a language param. So once change is occured it should looks like: www.website.com/xx I've read mostly all topics regarding Rect-Router v4 and history but all suggestions didn't work in my project. Some of them are related to obsolete functionality. I've also tried few example with withRouter, but nothing worked...

How it could be achieved in my case?

index.js:

import { Router, Route, Switch } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
const customHistory = createBrowserHistory();
...
    return (
      <I18nextProvider i18n={i18n}>
        <div>
          <Router history={customHistory}>
            <Switch>
              <Route exact path="/:lng?" component={Page} />
              <Route path={`/:lng?/someURL`} component={Page}/>
               ...
              <Route component={NoMatch} />
            </Switch>
          </Router>
          <ModalContainer />
        </div>
      </I18nextProvider>
    )

navigation component:

  handleClick() {   
   **append URL with lang param**
    console.log(history);    
    -> history: History { length: 1, scrollRestoration: "auto", state: null }
    history.push('/redirected');
    -> TypeError: history.push is not a function
  }

  render() {
    const toggle = lng => i18n.changeLanguage(lng);    

      return (        
        <a href="javascript:void(0)" onClick={()=>{ toggle(this.props.event); this.handleClick(); }}></a>
      )

Should it be done with function as handleClick() or this event should be global? The languages are switched from several components.

React-Router V4.2.0

2 Answers

Your navigation component needs to use a Link or NavLink component from react-router. There is no need for you manually access the router from context.

import {NavLink} from 'react-router-dom';
class NavComponent extends React.Component {      

  render() {    
    const { i18n } = this.props;
    const toggle = lng => i18n.changeLanguage(lng);    

    if (this.props.event) {
      return (        
        <li><NavLink className={(this.props.spanClassName)} onClick={()=> toggle(this.props.event)} to={this.props.event}/></li>
      );
    }
    else
    return null;

  }
};

well, I came to this solution. Maybe it will be helpful for some people. If there are more better ways to achieve it, please post your answer.

But one problem left. Once I'm in www.website.com/xx/someURL and press language switch the xx part of the URL should be swapped with new param but someURL should remain. Anybody knows how to make it?

index.js:

import { Router, Route, Switch } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
const customHistory = createBrowserHistory();

  render() {       
    return (
      <I18nextProvider i18n={i18n}>
        <div>
          <Router history={customHistory}>
            <Switch>
              <Route exact path="/:lng?" component={Page} />
              <Route path={`/:lng?/someURL`} component={Page}/>
              <Route component={NoMatch} />
            </Switch>
          </Router>
          <ModalContainer />
        </div>
      </I18nextProvider>
    )
  }

nav component:

import PropTypes from "prop-types";

class NavLink extends React.Component {
  static contextTypes = {
    router: PropTypes.object
  }
  constructor(props, context) {
     super(props, context);
  }


  handleClick(path) {    
    this.context.router.history.push(path);
  }

  render() {    
    const { i18n } = this.props;
    const toggle = lng => i18n.changeLanguage(lng);    

    if (this.props.event) {

      return (        
        <li><a href="javascript:void(0)" onClick={()=>{ toggle(this.props.event); this.handleClick(this.props.event); }}><span className={(this.props.spanClassName)}></span></a></li>
      )
    }

  }
};
Related