React Native: переключение видимости ящика и панели навигации на маршрутах

У меня есть приложение, которое нужно Drawer & Navigator, Для этого у меня есть родительский компонент Drawer в index.ios.js, который инкапсулирует все остальное.

Теперь я использую вариант использования: я должен отключить Drawer или скрыть NavigationBar на некоторых маршрутах. Чтобы достичь этого, я обновляю состояние родительского компонента и перерисовываю его при каждой навигации. Однако это, похоже, не помогает.

index.io.js:

class MyApp extends Component{

  state = {drawerEnabled:true, navigationBarEnabled: true};

  constructor(){
    super();
    this.navigate = this.navigate.bind(this);
    this.navigateForDrawer = this.navigateForDrawer.bind(this);
    this.getNavigator = this.getNavigator.bind(this);
    this.renderScene = this.renderScene.bind(this);
  }

  navigateForDrawer(route) {
      this.refs.navigator.resetTo(route);
      this.refs.drawer.close();
  }

  getNavigationBar(){
    return (
      this.state.navigationBarEnabled ?
      <Navigator.NavigationBar
      style={styles.navBar}
      transitionStyles={Navigator.NavigationBar.TransitionStylesIOS}
      routeMapper={NavigationBarRouteMapper}
      /> : null);
  }

  getNavigator(){
    return (
      <Navigator
        ref="navigator"
        style={styles.navigator}
        initialRoute={{id:'Login'}}
        renderScene={this.renderScene}
        navigationBar={this.getNavigationBar()}
      />
    );
  }

  navigate(route, method){
    if(route)
    switch (route.id) {
        case 'Login':
        this.state = {drawerEnabled: false, navigationBarEnabled: false};
        break;

        case 'NewBooking':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'MyBookings':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'AboutUs':
        this.state = {drawerEnabled: true, navigationBarEnabled: true};
        break;

        case 'ConfirmBooking':
        this.state = {drawerEnabled: false, navigationBarEnabled: true};
        break;

        case 'BookingComplete':
        this.state = {drawerEnabled: false, navigationBarEnabled: true};
        break;
    }

      this.forceUpdate();
      this.refs.navigator[method](route);
  }

  renderScene(route, navigator){
    navigator.navigate = this.navigate;
    switch (route.id) {
        case 'Login':
        return <Login navigate={this.navigate}/>;

        case 'NewBooking':
        route.title = 'New Booking';
        return <NewBooking navigate={this.navigate}/>;

        case 'MyBookings':
        route.title = 'My Bookings';
        return <MyBookings navigate={this.navigate}/>;

        case 'AboutUs':
        route.title = 'About Us';
        return <AboutUs navigate={this.navigate}/>;

        case 'ConfirmBooking':
        route.title = 'Booking Summary';
        return <ConfirmBooking navigate={this.navigate} booking={route.booking}/>;

        case 'BookingComplete':
        route.title = 'Booking Complete';
        return <BookingComplete navigate={this.navigate} booking={route.booking}/>;

      default:
    }
  }

  render() {
    return (
      <Drawer
        ref="drawer"
        disabled={!this.state.drawerEnabled}
        content={<ControlPanel navigate={this.navigateForDrawer}/>}
        tapToClose={true}
        openDrawerOffset={0.2} // 20% gap on the right side of drawer
        panCloseMask={0.2}
        closedDrawerOffset={-3}
        styles={{
          main: {paddingLeft: 3}
        }}
        tweenHandler={(ratio) => ({
          main: { opacity:(2-ratio)/2 }
        })}
        >
        {this.getNavigator()}
      </Drawer>
    )
  }

}

Чтобы перейти к новому маршруту:

this.props.navigate({id: 'ConfirmBooking', booking: booking}, 'push');

PS: Ящик, который я использую, является реагирующим

2 ответа

Я предполагаю, что вы имеете в виду DrawerLayoutAndroid ( или DrawerLayout) в качестве компонента ящика.

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

this.refs.drawer.openDrawer(); // opens the drawer
this.refs.drawer.closeDrawer(); // closes the drawer

Я бы включил эти вызовы в вашу функцию навигации, это, кажется, лучшее место.

Я должен был сделать то же самое с панелью навигации, появляющейся на определенных страницах, и я сделал следующее:

Сначала я написал свой собственный NavBar, который просто экран с flex:1 и имеет два внутренних компонента. Во-первых, это панель навигации, которая имеет flex:1 и содержит три внутренних компонента, левую кнопку, заголовок и правую кнопку с flex:1, flex:2, flex:1 соответственно. Таким образом, у меня может быть настраиваемая панель навигации на каждом экране.

Вот код:

React.createClass({

   propTypes: {

   /* Specifies the background color of the navigation bar*/
   backgroundColor: React.PropTypes.string,

   /* The view to be displayed */
   view: React.PropTypes.element,

   /* A component representing the left button */
   leftButton: React.PropTypes.element,

   /* Title component that shows up in the middle of the screen */
   title: React.PropTypes.element,

   /* Right button component */
   rightButton: React.PropTypes.element,

   /* including a modal ensures that the modal is centered in the screen */
   modal: React.PropTypes.element,

   /* Callback that is called from the Navigator bar screen. Any arguments
    * passed, represent the positioning and dimensions of the Navigation Bar
    */
   onLayout: React.PropTypes.func,

   /*The separator that shows up between the Navigation Bar and the view*/
   separator: React.PropTypes.element,
   },

   render(){
     return (
       <View style={styles.modalWrapper}>
         <View
           style={styles.container}>
              <View
             style={[styles.navBar, {backgroundColor: this.props.backgroundColor}]}
             onLayout={(e) =>{
             this.props.onLayout && this.props.onLayout(e)
             }}>
          <View
            style={styles.leftButton}>
            {this.props.leftButton}
          </View>
          <View
            style={styles.title}>
            {this.props.title }
          </View>
          <View
            style={styles.rightButton}>
            {this.props.rightButton}
          </View>
        </View>

        <View style={styles.viewWrapper}>
          {this.props.separator }
          {this.props.view}
        </View>
    </View>
    {this.props.modal}
  </View>);
  },
});

И стили:

StyleSheet.create({
  modalWrapper: {
    width: device.width,
    height: device.height
  },
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  navBar: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center'
  },
  viewWrapper: {
    flex: 8
  },
  rightButton: {
    top: STATUS_BAR_OFFSET,
    flex: 1,
    alignItems: 'flex-end',
    justifyContent: 'center',
    paddingRight: PADDING,
  },
  leftButton: {
    top: STATUS_BAR_OFFSET,
    flex: 1,
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingLeft: PADDING,
  },
  title: {
    top: STATUS_BAR_OFFSET,
    flex: 2,
    alignItems: 'center',
    justifyContent: 'center',
    paddingRight: PADDING / 2,
    paddingLeft: PADDING / 2,
  }
});

onStartShouldSetResponderCapture

Далее, хотя у меня еще не было изменений, чтобы попробовать это, я бы предложил usint onStartShouldSetResponderCapture, Если вы попытаетесь запустить следующий пример, вы увидите порядок, в котором вложенные представления фиксируют события касания. Под реагируют родные onStartShouldSetResponder начинается с самого нижнего дочернего представления, и события касания всплывают до самого верха, если ни один дочерний элемент не отвечает. Тем не мение, onStartShouldSetResponderCapture вызывается первым на самом внешнем виде.

React.createClass({
  render() {
    return (
      <View onStartShouldSetResponderCapture={() => this.text('1')}
            onStartShouldSetResponder={() => this.text('4')}
            style={{flex:1}}>
        <View onStartShouldSetResponderCapture={() => this.text('2')}
              onStartShouldSetResponder={() => this.text('3')}
              style={{flex:1}}>
        </View>
      </View>
    );


  },

  text(text) {
    console.log(text);
    //return true; // comment this to see the order or stop at '1'
  }
});

поэтому в вашем случае я бы сделал следующее:

render() {
        return (
          <View onStartShouldSetResponderCapture={() => !this.state.drawerEnabled}>
            <Drawer
              ref="drawer"
              disabled={!this.state.drawerEnabled}
              content={<ControlPanel navigate={this.navigateForDrawer}/>}
              tapToClose={true}
              openDrawerOffset={0.2} // 20% gap on the right side of drawer
              panCloseMask={0.2}
              closedDrawerOffset={-3}
              styles={{
          main: {paddingLeft: 3}
        }}
              tweenHandler={(ratio) => ({
          main: { opacity:(2-ratio)/2 }
        })}
            >
              {this.getNavigator()}
            </Drawer>
          </View>
        );
      }

SetState

Наконец, вместо того, чтобы звонить forceUpdate() Я бы позвонил this.setState({drawerEnabled: true}), Кроме того, не забудьте связать в своем конструкторе.

Другие вопросы по тегам