Программно перемещаться с помощью реактивного маршрутизатора
Я разрабатываю приложение, в котором я проверяю, не является ли пользователь 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 }) => {
...
})