【问题标题】:Navigate to a new page after Onbording with React-Native使用 React-Native Onbording 后导航到新页面
【发布时间】:2017-07-03 15:42:50
【问题描述】:

案例: 这是我的 Swipper 组件,我在其中创建和渲染通向 HomeScreen 的 Onboarding 屏幕的所有逻辑。我现在正在尝试为其添加导航,但遇到了一些我无法弄清楚的错误。

预期结果 显示第三个入职屏幕后,“Star Now”按钮应导航到主屏幕。

实际结果 按下第三个屏幕上的按钮后,我收到以下错误消息: 找不到变量:导航

我在 Expo 上运行它,如果你想试试的话,这里是链接。 https://expo.io/@jfsene/onboarding

/**
 * Swiper
 * Renders a swipable set of screens passed as children,
 * pagination indicators and a button to swipe through screens
 * or to get out of the flow when the last screen is reached
 */

import React, { Component } from 'react';
import {
  Dimensions,       // Detects screen dimensions
  Platform,         // Detects platform running the app
  ScrollView,       // Handles navigation between screens
  StyleSheet,       // CSS-like styles
  View,             // Container component
} from 'react-native';
import { StackNavigator } from 'react-navigation'; 
import Button from '../components/Button';
import HomeScreen from "../screens/HomeScreen";
import Screens from "../screens/Screens";

const App = StackNavigator({
  Home: { screen: HomeScreen },
});

// Detect screen width and height
const { width, height } = Dimensions.get('window');

export default class OnboardingScreens extends Component {

  // Props for ScrollView component
  static defaultProps = {
    // Arrange screens horizontally
    horizontal: true,
    // Scroll exactly to the next screen, instead of continous scrolling
    pagingEnabled: true,
    // Hide all scroll indicators
    showsHorizontalScrollIndicator: false,
    showsVerticalScrollIndicator: false,
    // Do not bounce when the end is reached
    bounces: false,
    // Do not scroll to top when the status bar is tapped
    scrollsToTop: false,
    // Remove offscreen child views
    removeClippedSubviews: true,
    // Do not adjust content behind nav-, tab- or toolbars automatically
    automaticallyAdjustContentInsets: false,
    // Fisrt is screen is active
    index: 0
  };

  state = this.initState(this.props);

  /**
   * Initialize the state
   */
  initState(props) {

    // Get the total number of slides passed as children
    const total = props.children ? props.children.length || 1 : 0,
      // Current index
      index = total > 1 ? Math.min(props.index, total - 1) : 0,
      // Current offset
      offset = width * index;

    const state = {
      total,
      index,
      offset,
      width,
      height,
    };

    // Component internals as a class property,
    // and not state to avoid component re-renders when updated
    this.internals = {
      isScrolling: false,
      offset
    };

    return state;
  }

  /**
   * Scroll begin handler
   * @param {object} e native event
   */
  onScrollBegin = e => {
    // Update internal isScrolling state
    this.internals.isScrolling = true;
  }

  /**
   * Scroll end handler
   * @param {object} e native event
   */
  onScrollEnd = e => {
    // Update internal isScrolling state
    this.internals.isScrolling = false;

    // Update index
    this.updateIndex(e.nativeEvent.contentOffset
      ? e.nativeEvent.contentOffset.x
      // When scrolled with .scrollTo() on Android there is no contentOffset
      : e.nativeEvent.position * this.state.width
    );
  }

  /*
   * Drag end handler
   * @param {object} e native event
   */
  onScrollEndDrag = e => {
    const { contentOffset: { x: newOffset } } = e.nativeEvent,
      { children } = this.props,
      { index } = this.state,
      { offset } = this.internals;

    // Update internal isScrolling state
    // if swiped right on the last slide
    // or left on the first one
    if (offset === newOffset &&
      (index === 0 || index === children.length - 1)) {
      this.internals.isScrolling = false;
    }
  }

  /**
   * Update index after scroll
   * @param {object} offset content offset
   */
  updateIndex = (offset) => {
    const state = this.state,
      diff = offset - this.internals.offset,
      step = state.width;
    let index = state.index;

    // Do nothing if offset didn't change
    if (!diff) {
      return;
    }

    // Make sure index is always an integer
    index = parseInt(index + Math.round(diff / step), 10);

    // Update internal offset
    this.internals.offset = offset;
    // Update index in the state
    this.setState({
      index
    });
  }

  /**
   * Swipe one slide forward
   */

  navigate = () => {
     const { navigate } = this.props.navigation;
     return;
  }
  swipe = () => {
    // Ignore if already scrolling or if there is less than 2 slides
    if (this.internals.isScrolling || this.state.total < 2) {
      return;
    }

    const state = this.state,
      diff = this.state.index + 1,
      x = diff * state.width,
      y = 0;

    // Call scrollTo on scrollView component to perform the swipe
    this.scrollView && this.scrollView.scrollTo({ x, y, animated: true });

    // Update internal scroll state
    this.internals.isScrolling = true;

    // Trigger onScrollEnd manually on android
    if (Platform.OS === 'android') {
      setImmediate(() => {
        this.onScrollEnd({
          nativeEvent: {
            position: diff
          }
        });
      });
    }
  }

  /**
   * Render ScrollView component
   * @param {array} slides to swipe through
   */
  renderScrollView = pages => {
    return (
      <ScrollView ref={component => { this.scrollView = component; }}
        {...this.props}
        contentContainerStyle={[styles.wrapper, this.props.style]}
        onScrollBeginDrag={this.onScrollBegin}
        onMomentumScrollEnd={this.onScrollEnd}
        onScrollEndDrag={this.onScrollEndDrag}
      >
        {pages.map((page, i) =>
          // Render each slide inside a View
          <View style={[styles.fullScreen, styles.slide]} key={i}>
            {page}
          </View>
        )}
      </ScrollView>
    );
  }

  /**
   * Render pagination indicators
   */
  renderPagination = () => {
    if (this.state.total <= 1) {
      return null;
    }

    const ActiveDot = <View style={[styles.dot, styles.activeDot]} />,
      Dot = <View style={styles.dot} />;

    let dots = [];

    for (let key = 0; key < this.state.total; key++) {
      dots.push(key === this.state.index
        // Active dot
        ? React.cloneElement(ActiveDot, { key })
        // Other dots
        : React.cloneElement(Dot, { key })
      );
    }

    return (
      <View
        pointerEvents="none"
        style={[styles.pagination, styles.fullScreen]}
      >
        {dots}
      </View>
    );
  }

  /**
   * Render Continue or Done button
   */
  renderButton = () => {
    const lastScreen = this.state.index === this.state.total - 1;
    return (
      <View pointerEvents="box-none" style={[styles.buttonWrapper, styles.fullScreen]}>
        {lastScreen
          // Show this button on the last screen
          // TODO: Add a handler that would send a user to your app after onboarding is complete
          ? <Button text="Start Now" onPress={() => navigate('Home')} />
          // Or this one otherwise
          : <Button text="Continue" onPress={() => this.swipe()} />
        }
      </View>
    );
  }

  /**
   * Render the component
   */

  render = ({ children } = this.props) => {
    return (
      <View style={[styles.container, styles.fullScreen]}>
        {/* Render screens */}
        {this.renderScrollView(children)}
        {/* Render pagination */}
        {this.renderPagination()}
        {/* Render Continue or Done button */}
        {this.renderButton()}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  // Set width and height to the screen size
  fullScreen: {
    width: width,
    height: height
  },
  // Main container
  container: {
    backgroundColor: 'transparent',
    position: 'relative'
  },
  // Slide
  slide: {
    backgroundColor: 'transparent'
  },
  // Pagination indicators
  pagination: {
    position: 'absolute',
    bottom: 110,
    left: 0,
    right: 0,
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'flex-end',
    backgroundColor: 'transparent'
  },
  // Pagination dot
  dot: {
    backgroundColor: 'rgba(0,0,0,.25)',
    width: 8,
    height: 8,
    borderRadius: 4,
    marginLeft: 3,
    marginRight: 3,
    marginTop: 3,
    marginBottom: 3
  },
  // Active dot
  activeDot: {
    backgroundColor: '#FFFFFF',
  },
  // Button wrapper
  buttonWrapper: {
    backgroundColor: 'transparent',
    flexDirection: 'column',
    position: 'absolute',
    bottom: 0,
    left: 0,
    flex: 1,
    paddingHorizontal: 10,
    paddingVertical: 40,
    justifyContent: 'flex-end',
    alignItems: 'center'
  },
});

【问题讨论】:

  • 请从您的代码中显示更多 StackNavigator 以查看您的导航结构。

标签: reactjs react-native react-router react-navigation


【解决方案1】:

如果你想从 OnboardingScreens 导航到 HomeScreen,也应该在 StackNavigator 中添加 OnboardingScreens。

【讨论】:

    【解决方案2】:

    你需要添加一个导航道具

    /**
     * Swiper
     * Renders a swipable set of screens passed as children,
     * pagination indicators and a button to swipe through screens
     * or to get out of the flow when the last screen is reached
     */
    
    import React, {
      Component
    } from 'react';
    import {
      Dimensions, // Detects screen dimensions
      Platform, // Detects platform running the app
      ScrollView, // Handles navigation between screens
      StyleSheet, // CSS-like styles
      View, // Container component
    } from 'react-native';
    import PropTypes from 'prop-types';
    import Button from './Button';
    
    
    // Detect screen width and height
    const {
      width,
      height
    } = Dimensions.get('window');
    
    
    export default class Swiper extends Component {
    
      // Props for ScrollView component
      static defaultProps = {
    
        //Here is what you need
        onPressnav: PropTypes.func.isRequired,
    
        // Arrange screens horizontally
        horizontal: true,
        // Scroll exactly to the next screen, instead of continous scrolling
        pagingEnabled: true,
        // Hide all scroll indicators
        showsHorizontalScrollIndicator: false,
        showsVerticalScrollIndicator: false,
        // Do not bounce when the end is reached
        bounces: false,
        // Do not scroll to top when the status bar is tapped
        scrollsToTop: false,
        // Remove offscreen child views
        removeClippedSubviews: true,
        // Do not adjust content behind nav-, tab- or toolbars automatically
        automaticallyAdjustContentInsets: false,
        // Fisrt is screen is active
        index: 0
      };
    
      state = this.initState(this.props);
    
      /**
       * Initialize the state
       */
      initState(props) {
    
        //const {goBack} = this.props.navigation;
        // Get the total number of slides passed as children
        const total = props.children ? props.children.length || 1 : 0,
          // Current index
          index = total > 1 ? Math.min(props.index, total - 1) : 0,
          // Current offset
          offset = width * index;
    
    
    
        const state = {
          total,
          index,
          offset,
          width,
          height,
        };
    
        // Component internals as a class property,
        // and not state to avoid component re-renders when updated
        this.internals = {
          isScrolling: false,
          offset
        };
    
        return state;
      }
    
      /**
       * Scroll begin handler
       * @param {object} e native event
       */
      onScrollBegin = e => {
        // Update internal isScrolling state
        this.internals.isScrolling = true;
      }
    
      /**
       * Scroll end handler
       * @param {object} e native event
       */
      onScrollEnd = e => {
        // Update internal isScrolling state
        this.internals.isScrolling = false;
    
        // Update index
        this.updateIndex(e.nativeEvent.contentOffset ?
          e.nativeEvent.contentOffset.x
          // When scrolled with .scrollTo() on Android there is no contentOffset
          :
          e.nativeEvent.position * this.state.width
        );
      }
    
      /*
       * Drag end handler
       * @param {object} e native event
       */
      onScrollEndDrag = e => {
        const {
          contentOffset: {
            x: newOffset
          }
        } = e.nativeEvent, {
          children
        } = this.props, {
          index
        } = this.state, {
          offset
        } = this.internals;
    
        // Update internal isScrolling state
        // if swiped right on the last slide
        // or left on the first one
        if (offset === newOffset &&
          (index === 0 || index === children.length - 1)) {
          this.internals.isScrolling = false;
        }
      }
    
      /**
       * Update index after scroll
       * @param {object} offset content offset
       */
      updateIndex = (offset) => {
        const state = this.state,
          diff = offset - this.internals.offset,
          step = state.width;
        let index = state.index;
    
        // Do nothing if offset didn't change
        if (!diff) {
          return;
        }
    
        // Make sure index is always an integer
        index = parseInt(index + Math.round(diff / step), 10);
    
        // Update internal offset
        this.internals.offset = offset;
        // Update index in the state
        this.setState({
          index
        });
      }
    
      navigatet = () => {
        console.log('This has been Clicked');
    
      }
    
      /**
       * Swipe one slide forward
       */
      swipe = () => {
        // Ignore if already scrolling or if there is less than 2 slides
        if (this.internals.isScrolling || this.state.total < 2) {
          return;
        }
    
        const state = this.state,
          diff = this.state.index + 1,
          x = diff * state.width,
          y = 0;
    
        // Call scrollTo on scrollView component to perform the swipe
        this.scrollView && this.scrollView.scrollTo({
          x,
          y,
          animated: true
        });
    
        // Update internal scroll state
        this.internals.isScrolling = true;
    
        // Trigger onScrollEnd manually on android
        if (Platform.OS === 'android') {
          setImmediate(() => {
            this.onScrollEnd({
              nativeEvent: {
                position: diff
              }
            });
          });
        }
      }
    
      /**
       * Render ScrollView component
       * @param {array} slides to swipe through
       */
      renderScrollView = pages => {
        return ( <
          ScrollView ref = {
            component => {
              this.scrollView = component;
            }
          } { ...this.props
          }
          contentContainerStyle = {
            [styles.wrapper, this.props.style]
          }
          onScrollBeginDrag = {
            this.onScrollBegin
          }
          onMomentumScrollEnd = {
            this.onScrollEnd
          }
          onScrollEndDrag = {
            this.onScrollEndDrag
          } >
          {
            pages.map((page, i) =>
              // Render each slide inside a View
              <
              View style = {
                [styles.fullScreen, styles.slide]
              }
              key = {
                i
              } > {
                page
              } <
              /View>
            )
          } <
          /ScrollView>
        );
      }
    
      /**
       * Render pagination indicators
       */
      renderPagination = () => {
        if (this.state.total <= 1) {
          return null;
        }
    
        const ActiveDot = < View style = {
          [styles.dot, styles.activeDot]
        }
        />,
        Dot = < View style = {
          styles.dot
        }
        />;
    
        let dots = [];
    
        for (let key = 0; key < this.state.total; key++) {
          dots.push(key === this.state.index
            // Active dot
            ?
            React.cloneElement(ActiveDot, {
              key
            })
            // Other dots
            :
            React.cloneElement(Dot, {
              key
            })
          );
        }
    
        return ( <
          View pointerEvents = "none"
          style = {
            [styles.pagination, styles.fullScreen]
          } >
          {
            dots
          } <
          /View>
        );
      }
    
      /**
       * Render Continue or Done button
       */
      renderButton = () => {
        navigation = this.props.navigation
        const lastScreen = this.state.index === this.state.total - 1;
        return ( <
          View pointerEvents = "box-none"
          style = {
            [styles.buttonWrapper, styles.fullScreen]
          } > {
            lastScreen
            // Show this button on the last screen
            // TODO: Add a handler that would send a user to your app after onboarding is complete
            ?
            < Button text = "Start Now"
            onPress = {
              this.props.onPressnav
            }
            />
            // Or this one otherwise
            :
              < Button text = "Continue"
            onPress = {
              () => this.swipe()
            }
            />
          } <
          /View>
        );
      }
    
      /**
       * Render the component
       */
      render = ({
        children
      } = this.props) => {
    
        return ( <
          View style = {
            [styles.container, styles.fullScreen]
          } > { /* Render screens */ } {
            this.renderScrollView(children)
          } { /* Render pagination */ } {
            this.renderPagination()
          } { /* Render Continue or Done button */ } {
            this.renderButton()
          } <
          /View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      // Set width and height to the screen size
      fullScreen: {
        width: width,
        height: height
      },
      // Main container
      container: {
        backgroundColor: 'transparent',
        position: 'relative'
      },
      // Slide
      slide: {
        backgroundColor: 'transparent'
      },
      // Pagination indicators
      pagination: {
        position: 'absolute',
        bottom: 110,
        left: 0,
        right: 0,
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'flex-end',
        backgroundColor: 'transparent'
      },
      // Pagination dot
      dot: {
        backgroundColor: 'rgba(0,0,0,.25)',
        width: 8,
        height: 8,
        borderRadius: 4,
        marginLeft: 3,
        marginRight: 3,
        marginTop: 3,
        marginBottom: 3
      },
      // Active dot
      activeDot: {
        backgroundColor: '#5FD7F4',
      },
      // Button wrapper
      buttonWrapper: {
        backgroundColor: 'transparent',
        flexDirection: 'column',
        position: 'absolute',
        bottom: 0,
        left: 0,
        flex: 1,
        paddingHorizontal: 10,
        paddingVertical: 40,
        justifyContent: 'flex-end',
        alignItems: 'center'
      },
    });

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-13
      • 2021-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多