Подтвердить что ты не робот

Частные маршруты ретрансляции маршрутизатора/перенаправление не работают

Я немного скорректировал пример React Router для частных маршрутов, чтобы хорошо играть с Redux, но никакие компоненты не отображаются при связывании или перенаправлении на другие "страницы". Пример можно найти здесь:

https://reacttraining.com/react-router/web/example/auth-workflow

Их компонент PrivateRoute выглядит следующим образом:

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    fakeAuth.isAuthenticated ? (
      <Component {...props}/>
    ) : (
      <Redirect to={{
        pathname: '/login',
        state: { from: props.location }
      }}/>
    )
  )}/>
)

Но, поскольку я включил его в приложение Redux, мне пришлось немного настроить PrivateRoute, чтобы я мог получить доступ к хранилищу редуктов, а также к поисковому сервису:

const PrivateRouteComponent = (props) => (
    <Route {...props.routeProps} render={() => (
    props.logged_in ? (
        <div>{props.children}</div>
        ) : (
        <Redirect to={{
            pathname: '/login',
            state: { from: props.location }
        }} /> )
    )} />
);

const mapStateToProps = (state, ownProps) => {
    return {
        logged_in: state.auth.logged_in,
        location: ownProps.path,
        routeProps: {
            exact: ownProps.exact,
            path: ownProps.path
        }
    };
};

const PrivateRoute = connect(mapStateToProps, null)(PrivateRouteComponent);
export default PrivateRoute

Всякий раз, когда я не вошел в систему и не попал в PrivateRoute, я правильно перенаправлен в /login. Однако после того, как, например, при входе и использовании <Redirect .../> или нажатии любой <Link ...> на PrivateRoute, URI обновляется, но в представлении нет. Он остается на одном компоненте.

Что я делаю неправильно?


Чтобы завершить изображение, в приложении index.js есть некоторые нерелевантные вещи, и маршруты настраиваются следующим образом:

ReactDOM.render(
    <Provider store={store}>
        <App>
            <Router>
                <div>
                    <PrivateRoute exact path="/"><Home /></PrivateRoute>
                    // ... other private routes
                    <Route path="/login" component={Login} />
                </div>
            </Router>
        </App>
    </Provider>,
    document.getElementById('root')
);
4b9b3361

Ответ 1

Установите частный маршрут, чтобы он не был чистым:

export default connect(mapStateToProps, null, null, {
  pure: false,
})(PrivateRoute);

Это позволит повторному рендерингу компонента.

Смотрите: react-router-4-x-privateroute-not-working-after-connecting-to-redux.

Ответ 2

У меня была такая же проблема, я решил ее, создав контейнер App redux и передав isAuthenticated как опору для PrivateRoute

Вот он, надеюсь, поможет

const App = (props) => {
return (
  <Provider store={store}>
    <Router>
      <div>
        <PrivateRoute path="/secured" component={Secured} isAuthenticated={props.isAuthenticated} />
      </div>
    </Router>
  </Provider>
  );
};

const mapStateToProps = state => ({
  isAuthenticated: state.isAuthenticated
});

export default connect(mapStateToProps)(App);

Затем в моем PrivateRoute

const PrivateRoute = ({ component: Component, isAuthenticated, ...rest}) => (
<Route
  {...rest}
  render={props => (
    isAuthenticated
    ? (
       <Component {...props} />
    )
    : (<Redirect to={{ pathname: '/login', state: { from: props.location} }} />)
  )}
/>
);

export default PrivateRoute;

Ответ 3

Вам нужно обернуть Route тегом <Switch>

ReactDOM.render(
<Provider store={store}>
    <App>
        <Router>
            <div>
                <Switch>
                   <PrivateRoute exact path="/"><Home /></PrivateRoute>
                   // ... other private routes
                   <Route path="/login" component={Login} />
                </Switch>
            </div>
        </Router>
    </App>
</Provider>,
document.getElementById('root'));

Ответ 4

Мне удалось получить эту работу с помощью параметра rest для доступа к данным из mapStateToProps:

const PrivateRoute = ({component: Component, ...rest}) => {
  const {isAuthenticated} = rest;

  return (
    <Route {...rest} render={props => (
      isAuthenticated ? (
        <Component {...props}/>
      ) : (
        <Redirect to={{
          pathname: '/login',
          state: {from: props.location}
        }}/>
      )
    )}
    />
  );
};

PrivateRoute.propTypes = {
  isAuthenticated: PropTypes.bool.isRequired,
};

function mapStateToProps(state) {
  return {
    isAuthenticated: state.user.isAuthenticated,
  };
}

export default connect(mapStateToProps)(PrivateRoute);

Ответ 5

Хорошо, я думаю, что ответ на этот вопрос должен быть более подробным, поэтому я здесь, после 4 часов рытья.

Когда вы завершаете свой компонент с помощью connect(), React Redux реализует mustComponentUpdate (sCU, если вы ищете ответы на вопросы github), и делаете мелкое сравнение в реквизитах (он пропускает каждую клавишу в реквизите объекта и проверяет, являются ли значения идентичны с '==='). На практике это означает, что ваш компонент считается чистым. Теперь он изменится только тогда, когда его реквизит изменится и только тогда! Это ключевое сообщение здесь. Второе ключевое сообщение, React router работает с контекстом для передачи объектов местоположения, сопоставления и истории из компонента Router to Route. Он не использует реквизиты.

Теперь давайте посмотрим на практике, что произойдет, потому что, даже зная это, я считаю это довольно сложным:

  • Дело 1:

После подключения 3 ключа к вашим реквизитам: путь, компонент и auth (задается подключением). Таким образом, на самом деле, ваш завернутый компонент НЕ будет повторно отображен на изменениях маршрута, потому что это не волнует. Когда изменения маршрута, ваши реквизиты не меняются и не будут обновляться.

  • Случай 3:

Теперь у вас есть 4 ключа к вашим реквизитам после подключения: путь, компонент, auth и anyprop. Трюк здесь в том, что anyprop - это объект, который создается каждый раз при вызове компонента. Поэтому всякий раз, когда вы вызываете этот компонент, это сравнение делается: {a: 1} === {a: 1}, который (вы можете попробовать) дает вам ложь, поэтому ваш компонент теперь обновляется каждый раз. Обратите внимание, однако, что ваш компонент по-прежнему не заботится о маршруте, его дети делают.

  • Случай 2:

Теперь, когда тайна здесь, потому что я предполагаю, что вы вызываете эту строку в своем файле приложения, и там не должно быть ссылки на "auth" там, и вы должны иметь ошибку (по крайней мере, то, что я набираю). Я предполагаю, что "auth" в вашем файле приложения ссылается на определенный там объект.

Теперь что нам делать?

Здесь я вижу два варианта:

  • Сообщите React Redux, что ваш компонент не является чистым, это приведет к удалению инъекции sCU, и ваш компонент теперь будет правильно обновляться.

    connect (mapStateToProps, null, null, { чистый: false }) (PrivateRoute)

  • Используйте WithRouter(), что приведет к тому, что вы добавите объект местоположения, соответствия и истории к вашему компоненту в качестве реквизита. Теперь я не знаю внутренних компонентов, но я подозреваю, что React router не мутирует эти объекты, поэтому каждый раз, когда маршрут изменяется, его реквизит изменяется (sCU возвращает true), а ваш компонент правильно обновляется. Побочным эффектом этого является то, что ваше дерево React теперь загрязнено множеством материалов WithRouter и Route...

Ссылка на проблему github: Работа с блокировкой обновлений

Здесь вы можете видеть, что withRouter предназначен как быстрое исправление, но не рекомендуемое решение. использование pure: false не упоминается, поэтому я не знаю, насколько хорошо это исправление может быть.

Я нашел третье решение, хотя мне непонятно, действительно ли это лучшее решение, чем у Router, используя компонент более высокого порядка. Вы подключаете свой компонент более высокого порядка к магазину Redux, и теперь ваш маршрут не наплевает на то, что он делает, HOC имеет дело с ним.

import Notlogged from "./Notlogged";    
function Isloggedhoc({ wrap: Component, islogged, ...props }) {
      return islogged ? <Component {...props} /> : <Notlogged {...props} />;
    }

const mapState = (state, ownprops) => ({
      islogged: state.logged,
      ...ownprops
    });

export default connect(mapState)(Isloggedhoc);

в вашем приложении App.js

<Route path="/PrivateRoute" render={props => <Isloadedhoc wrap={Mycomponent} />} />

Вы даже можете сделать функцию curried, чтобы немного сократить ее:

function isLogged(component) {
  return function(props) {
    return <Isloadedhoc wrap={component} {...props} />;
  };
}

Используя его так:

<Route path="/PrivateRoute" render={isLogged(Mycomponent)} />

Ответ 6

У меня такая же проблема, как @Rein. В моем случае PrivateRoute выглядит почти так же, как в оригинальной версии, но только подключен к Redux и использует его вместо fakeAuth в исходном примере.

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
  <Route
   {...rest}
   render={props =>
   auth.isAuthenticated
    ? <Component {...props} />
    : <Redirect to={{ pathname: "/login" }} />}
  />
);

 PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
  component: PropTypes.func.isRequired
 }

 const mapStateToProps = (state, ownProps) => {
  return {
     auth: state.auth
  }
};

export default connect(mapStateToProps)(PrivateRoute);

Использование и результат: -

  • НЕ работает, но ожидает.
    • <PrivateRoute path="/member" component={MemberPage} />
  • работает, но НЕ желательно использовать это
    • <PrivateRoute path="/member" component={MemberPage} auth={auth} />
  • рабочая. ТОЛЬКО для работы, но НЕ желательно использовать вообще. Понимание из этого момента состоит в том, что если вы подключаете оригинальный PrivateRoute к Redux, вам нужно передать некоторые дополнительные реквизиты (любую опору), чтобы заставить PrivateRoute работать в противном случае, это не сработает. Любой, пожалуйста, дайте некоторый намек на это поведение. Это моя главная забота. Как новый вопрос в
    • <PrivateRoute path="/member" component={MemberPage} anyprop={{a:1}} />

Ответ 7

В соответствии с документацией по реагированию маршрутизатора вы можете просто обернуть функцию connect withRouter:

// before
export default connect(mapStateToProps)(Something)

// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))

Это сработало для меня, и мои взгляды начали обновляться вместе с маршрутами в этом случае.