Программно перемещаться с помощью реактивного маршрутизатора

Я разрабатываю приложение, в котором я проверяю, не является ли пользователь loggedIn. Мне нужно отобразить форму входа, иначе dispatch a action, которая изменит маршрут и загрузит другой компонент. Вот мой код:

    render() {
         if (isLoggedIn) {
             // dispatch an action to change the route
         }
         // return login component
         <Login />
    }

Как я могу достичь этого, так как я не могу изменить состояния внутри функции рендеринга.

Ответ 1

Учитывая, что вы используете react-router v4

Используйте свой компонент с withRouter и используйте history.push from withRouter чтобы изменить маршрут. Вы должны использовать withRouter только тогда, когда ваш компонент не получает реквизиты Router, это может произойти в случаях, когда ваш компонент является вложенным дочерним элементом компонента, представленного маршрутизатором, и вы не передали реквизиты маршрутизатора ему или когда компонент вообще не связан с маршрутизатором и отображается как отдельный компонент от маршрутов.

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     componenDidMount() {
        // get isLoggedIn from localStorage or API call
        if (isLoggedIn) {
             // dispatch an action to change the route
             this.props.history.push('/home');
        }
     }
     render() {
         // return login component
         return <Login />
    }
}


export default withRouter(App);

Важная заметка

Если вы используете withRouter для предотвращения блокировки обновлений с помощью shouldComponentUpdate, важно, чтобы withRouter оборачивал компонент, который реализует shouldComponentUpdate. Например, при использовании Redux:

//Это обходит shouldComponentUpdate

withRouter(connect(...)(MyComponent))

//Это не

connect(...)(withRouter(MyComponent))

или вы можете использовать Redirect

import {withRouter} from 'react-router';

class App extends React.Component {
     ...
     render() {
         if(isLoggedIn) {
              return <Redirect to="/home"/>
         }
         // return login component
         return <Login />
    }
}

react-router v2 or react-router v3 позволяет использовать context для динамического изменения маршрута, например

class App extends React.Component {
     ...
     render() {
         if (isLoggedIn) {
             // dispatch an action to change the route
             this.context.router.push('/home');
         }
         // return login component
         return <Login />
    }
}

App.contextTypes = {
    router: React.PropTypes.object.isRequired
}
export default App;

или использовать

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

Ответ 2

В реакторе версии 4:

import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'

const Example = () => (

  if (isLoggedIn) {
    <OtherComponent />

  } else {

    <Router>
      <Redirect push to="/login" />
      <Route path="/login" component={Login}/>
    </Router>

  }
)

const Login = () => (
    <h1>Form Components</h1>
    ...
)

export default Example;

Ответ 3

Другой альтернативой является обращение с использованием асинхронных действий типа Thunk (которые безопасны/могут иметь побочные эффекты).

Если вы используете Thunk, вы можете ввести тот же объект history в свой компонент <Router> и Thunk с помощью thunk.withExtraArgument, например:

import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
import { createBrowserHistory } from "history"
import { applyMiddleware, createStore } from "redux"
import thunk from "redux-thunk"

const history = createBrowserHistory()

const middlewares = applyMiddleware(thunk.withExtraArgument({history}))
const store = createStore(appReducer, middlewares)

render(
  <Provider store={store}
    <Router history={history}>
      <Route path="*" component={CatchAll} />
    </Router
  </Provider>,
appDiv)

Затем в ваших создателях действий будет создан экземпляр history, который можно безопасно использовать с ReactRouter, поэтому вы можете просто запустить обычное событие Redux, если вы не вошли в систему:

// meanwhile... in action-creators.js
export const notLoggedIn = () => {
  return (dispatch, getState, {history}) => {
    history.push(`/login`)
  }
}

Другим преимуществом этого является то, что URL теперь проще обрабатывать, поэтому мы можем помещать информацию о перенаправлении в строку запроса и т.д.

Вы можете попробовать выполнить эту проверку в своих методах рендеринга, но если это вызовет проблемы, вы можете подумать о том, чтобы сделать это в componentDidMount или в другом месте жизненного цикла (хотя я также понимаю, что хочу придерживаться функциональных компиляторов без состояния! )

Вы все еще можете использовать Redux и mapDispatchToProps, чтобы вставить создателя действия в свой составной компонент, поэтому ваш компонент все еще только слабо связан с Redux.

Ответ 4

Это моя ручка loggedIn. react-router v4

PrivateRoute - разрешить ввод path если пользователь вошел в систему, и сохранить токен в localStorge

    function PrivateRoute({ component: Component, ...rest }) {
     return (
        <Route
          {...rest}
          render={props => (localStorage.token) ? <Component {...props} /> : (
            <Redirect
              to={{
                pathname: '/signin',
                state: { from: props.location },
              }}
            />
          )
          }
        />
      );
    }

Определите все пути в вашем приложении здесь

export default (
  <main>
    <Switch>
      <Route exact path="/signin" component={SignIn} />
      <Route exact path="/signup" component={SignUp} />
      <PrivateRoute path="/" component={Home} />
    </Switch>
  </main>
);

Ответ 5

render(){ 

    return (
        <div>  
            { this.props.redirect ? <Redirect to="/" /> :'' } 
            <div>
                add here component codes
            </div>
        </div>
    );
} 

Ответ 6

Те, кто сталкивается с проблемами в реализации этого на реакции-маршрутизатор v4. Вот рабочее решение для навигации по приложению реагирования программно.

history.js

import createHistory from 'history/createBrowserHistory'

export default createHistory()

App.js ИЛИ Route.jsx. Передайте историю как опору вашему маршрутизатору.

import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
 <Route path="/test" component={Test}/>
</Router>

Вы можете использовать push() для навигации.

import history from './history' 

...

render() {
     if (isLoggedIn) {
         history.push('/test') // this should change the url and re-render Test component
     }
     // return login component
     <Login />
}

Все благодаря этому комментарию: https://github.com/ReactTraining/react-router/issues/3498#issuecomment-301057248

Ответ 7

Я бы предложил вам использовать подключенный-реагирующий-маршрутизатор https://github.com/supasate/connected-react-router, который помогает выполнять навигацию даже из редукторов/действий, если вы хотите. это хорошо документировано и легко настраивается

Ответ 8

Я смог использовать history в функциональном компоненте без сохранения состояния, используя withRouter следующим образом (необходимо игнорировать предупреждение о машинописи):

import { withRouter } from 'react-router-dom';

...

type Props = { myProp: boolean };

// @ts-ignore
export const MyComponent: FC<Props> = withRouter(({ myProp, history }) => {

...

})