2015-03-01 5 views
17

У меня есть обычная и частная область в моем приложении, и я хочу показать диалог модальности входа в систему в общедоступном виде. Модаль должен иметь свой собственный маршрут. Второй вариант использования - это модальный профиль в частной области.React router modal-only routes

Проблема заключается в том, что текущий вид в фоновом режиме исчезнет, ​​когда модален будет показан, поскольку модаль не является дочерним по отношению к текущему маршруту просмотров.

Поскольку я не хочу добавлять один и тот же модальный путь к любому возможному виду, вот вопрос: можно ли отделить модальные маршруты от своих родительских маршрутов и показать их везде в приложении без основного рендеринга содержимого? Каков наилучший подход для этого? Я нашел this issue, но, похоже, это не проблема.

Обновление браузера на модальном маршруте ничего не даст в фоновом режиме, но это проблема, с которой я мог бы жить.

+3

Путь вы описываете это звучит как Вход/модальный не действительно маршрут на всех, так почему вы пытаетесь так относиться к этому? Это действительно «состояние» пользовательского интерфейса, которое может отображаться на уровне приложения, например, всякий раз, когда это необходимо. – WiredPrairie

+0

Это не просто состояние, если вы хотите сделать более динамичный модальный, например '/ user /: uid/folder /: fid', и связать его по нескольким местам. Вход - всего лишь пример. – kromit

+0

Для модов не-входа в систему, откуда бы они получили свое состояние? детский маршрут или где-то еще? –

ответ

19

Я думаю, что ваш лучший вариант, вероятно, использовать либо скрытые state, либо строки запроса (для постоянных ссылок), либо и то, и другое, особенно если модальная мода (например, мода входа) может отображаться на любой странице. На всякий случай, когда вы не знаете, React Router предоставляет часть истории API state, позволяющую хранить данные в истории пользователя, которые на самом деле не видны в URL-адресе.

Вот набор маршрутов, которые я имею в виду; вы можете прыгать straight into the working example on JSBin, если хотите. Вы также можете просмотреть полученный в результате пример app in its own window, чтобы вы могли видеть изменение URL-адресов (он использует стратегию размещения хэша для совместимости с JSBin) и чтобы убедиться, что обновление работает так, как вы ожидали.

const router = (
    <Router> 
    <Route component={LoginRedirect}> 
     <Route component={LocationDisplay}> 
     <Route path="/" component={ModalCheck}> 
      <Route path="/login" component={makeComponent("login")} /> 
      <Route path="/one" component={makeComponent("one")} /> 
      <Route path="/two" component={makeComponent("two")} /> 
      <Route path="/users" component={makeComponent("users")}> 
      <Route path=":userId" component={UserProfileComponent} /> 
      </Route> 
     </Route> 
     </Route> 
    </Route> 
    </Router> 
); 

Давайте исследовать эти маршруты и их компоненты.

Прежде всего, makeComponent - это всего лишь метод, который берет строку и создает компонент React, который отображает эту строку как заголовок, а затем все его дочерние элементы; это просто быстрый способ создания компонентов.

LoginRedirect является компонентом с одной целью: проверить, если путь /loginили если есть строка ?login запроса на пути тока. Если любое из этих значений истинно, и текущее состояние не содержит ключа login, оно устанавливает login ключ в состоянии true. Маршрут используется, если соответствует любому дочернему маршруту (то есть компонент всегда отображается).

class LoginRedirect extends React.Component { 
    componentWillMount() { 
    this.handleLoginRedirect(this.props); 
    } 

    componentWillReceiveProps(nextProps) { 
    this.handleLoginRedirect(nextProps); 
    } 

    handleLoginRedirect(props) { 
    const { location } = props; 
    const state = location.state || {}; 
    const onLoginRoute = location.query.login || location.pathname === "/login"; 
    const needsStateChange = onLoginRoute && !state.login; 
    if (needsStateChange) { 
     // we hit a URL with ?login in it 
     // replace state with the same URL but login modal state 
     props.history.setState({login: true}); 
    } 
    } 

    render() { 
    return React.Children.only(this.props.children); 
    } 
} 

Если вы не хотите использовать строки запроса для показа входа модальности, вы, конечно, можете изменить этот компонент в соответствии с вашими потребностями.

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

Состояние входа в систему важна для следующего компонента: ModalCheck. Этот компонент отвечает за проверку текущего состояния для login (или profile или, возможно, любых других) ключей и, при необходимости, отображения соответствующего модального кода.(Демо JSBin реализует супер простую модальность, ваш, конечно, будет лучше. :) Это также показывает статус модальных проверок в текстовом виде на главной странице.)

class ModalCheck extends React.Component { 
    render() { 
    const location = this.props.location; 
    const state = location.state || {}; 
    const showingLoginModal = state.login === true; 
    const showingProfileMoal = state.profile === true; 

    const loginModal = showingLoginModal && <Modal location={location} stateKey="login"><LoginModal /></Modal>; 
    const profileModal = showingProfileMoal && <Modal location={location} stateKey="profile"><ProfileModal /></Modal>; 

    return (
     <div style={containerStyle}> 
     <strong>Modal state:</strong> 
     <ul> 
      <li>Login modal: {showingLoginModal ? "Yes" : "No"}</li> 
      <li>Profile modal: {showingProfileMoal ? "Yes" : "No"}</li> 
     </ul> 
     {loginModal} 
     {profileModal} 
     {this.props.children} 
     </div> 
    ) 
    } 
} 

Все остальное является довольно стандартным React Router вещи. Единственное, что следует обратить внимание, это Link s внутри LocationDisplay, которые показывают, как вы можете ссылаться на различные места в вашем приложении, показывая модальности при определенных обстоятельствах.

Прежде всего, вы можете, конечно, ссылки (и постоянная ссылка) на любую страницу с просьбой показать логин модальность с помощью login ключа в строке запроса:

<Link to="/one" query={{login: 1}}>/one?login</Link> 
<Link to="/two" query={{login: 1}}>/two?login</Link> 

Вы также можете, конечно, , ссылка на адрес /login.

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

<Link to="/one" state={{login: true}}>/one with login state</Link> 
<Link to="/two" state={{login: true}}>/two with login state</Link> 

Вы можете также ссылку на страницу ток, добавления конкретных модальный.

const path = props.location.pathname; 

<Link to={path} state={{login: true}}>current path + login state</Link> 
<Link to={path} state={{profile: true}}>current path + profile state</Link> 

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

<Route path="https://stackoverflow.com/users/:id" component={UserPage}> 
    <Route path="https://stackoverflow.com/users/:id/profile" component={UserProfile} /> 
</Route> 

UserProfile, в этом случае, будет компонентом, который оказывает модальность.

Другим примером, где вы можете внести изменения, является хранение определенных модалов в истории; если вы этого не хотите, используйте replaceState вместо setState, если необходимо.

+0

Очень хорошее решение. Спасибо. Тем не менее, мне все еще интересно, почему у реагирующего маршрутизатора нет такого встроенного. – kromit

+0

Этот ответ по-прежнему действителен (и очень информативен), но у JS Bin есть несколько мертвых скриптов. Я обновил сценарии истории и реагирования, чтобы указать на последние скрипты на unpkg.com https://jsbin.com/laxahozaro/edit?js,output – yourfavorite

0

Использование shouldComponentUpdate сделать это будет очень просто

<Router history={newHistory}> 
 
     <Route path="/" component={App}> 
 
      <Route path="home" component={Container} > 
 
      <IndexRedirect to="home"/> 
 
      <Route path="login" component={Authentication}/> 
 
      <Route path="home" component={Home}/> 
 
      <Route path="post/:id" component={Post}/> 
 
      </Route> 
 
     </Route> 
 
</Router> 
 

 

 
<Link to='/post/123' state={{isModal:true}}/> 
 
<Link to='/home' state={{isModal:false}}/> 
 
<Link to='/login' state={{isModal:true}}/> 
 

 

 
Container = React.createClass({ 
 
    render() { 
 
    let isModal = (location.state && location.state.modal); 
 
     return (
 
     <div id="MeContainer"> 
 
      <ModalControlOpen isModal={isModal}> 
 
      <Modal returnTo={this.props.location.pathname}> 
 
       {this.props.children} 
 
      </Modal> 
 
      </ModalControlOpen> 
 
      <ModalControlClose isModal={isModal}> 
 
      {this.props.children} 
 
      </ModalControlClose> 
 
     </div> 
 
    ) 
 
    
 
    } 
 
}); 
 

 
ModalControlOpen = React.createClass({ 
 
    render(){ 
 
    if (this.props.isModal) { 
 
     return (
 

 
     this.props.children 
 
    ) 
 
    } else return <div></div> 
 

 
    } 
 
}); 
 

 
ModalControlClose = React.createClass({ 
 
    shouldComponentUpdate(nextProp){ 
 
    return !nextProp.isModal 
 
    }, 
 
    render(){ 
 
    return (
 
     this.props.children 
 
    ) 
 
    } 
 
});

+2

, пожалуйста, немного объясните свой код. Это улучшит ваш ответ. – davejal