导航实验:如何将道具传递给场景?

Navigation Experimental: How to pass props to scenes?

我正在处理初始化数据的应用程序启动画面。

想要的结果就是:

  1. 在 componentWillMount 上加载数据:视图显示 loading indicator
  2. 加载数据时:视图显示 check icon
  3. 1秒后:导航到另一个场景

为了将显示 loading indicator 的视图更改为 check icon,我需要更新传递给闪屏场景的 dataReady 道具 但我不知道如何在不更新导航状态的情况下执行此操作(即通过导航状态的 route 对象将道具传递给场景)。

这是我的尝试(没有按预期工作):

class MainRouter extends React.Component {

  componentWillMount() {
    // Load data.
    this.props.initApp();
  }
  componentWillReceiveProps(nextProps) {
    // initAppReady is set to true 1 second after data loaded.
    if (nextProps.initAppReady) {
      this.props.handleNavigation({ type: 'PUSH', route: { key: 'home' } });
    }
  }

  renderScene = props => {
    switch (props.scene.key) {
      case 'scene_splash_screen':
        // dataReady is set to true after data loaded.
        return (<SplashScreen dataReady={this.props.dataReady} />);

      case 'scene_home':
        return (<Home />);

      default:
        return null;
    }
  }

  render() {
    return (
      <NavigationCardStack
        direction={'vertical'}
        navigationState={this.props.mainRoutes}
        renderScene={this.renderScene}
      />
    );
  }
}

因此,renderScene 方法在 props.dataReady 更改时不会被调用,因此 SplashScreen 场景不会相应更新。

您应该创建一个新的 AppLoader 组件,它显示初始屏幕并执行初始数据加载。此外,不是将 initAppReady 作为 prop 传递给路由器,而是应该让 initApp 函数 return 一个承诺,它在应用程序准备就绪时解决,然后 AppLoader 可以做一秒停顿,导航到首页场景。

注意:我注意到您将 home 路线压入堆栈。我建议你用 home route 替换 stack 。如果您继续前进,那么用户可能会不小心导航回您的初始屏幕,这可能不是您想要的。

这是我的意思的一个例子:

MainRouter.js

class MainRouter extends React.Component {

  renderScene = props => {
    switch (props.scene.key) {
      case 'scene_splash_screen':
        return (<AppLoader initApp={this.props.initApp} handleNavigation={this.props.handleNavigation} />);

      case 'scene_home':
        return (<Home />);

      default:
        return null;
    }
  }

  render() {
    return (
      <NavigationCardStack
        direction={'vertical'}
        navigationState={this.props.mainRoutes}
        renderScene={this.renderScene}
      />
    );
  }
}

AppLoader.js

const AFTER_INIT_DELAY = 1000;
class AppLoader extends React.Component {

  static propTypes = {
    initApp: PropTypes.func,
    handleNavigation: PropTypes.func,
  };

  constructor(props) {
    super(props);
    this.state = {
      dataReady: false,
    };
  }

  componentWillMount() {
    this.props.initApp()
      .then(() => {
        this.setState({dataReady: true}, this.afterAppInit);
      })
      .catch(() => {
        // Do something if the app init fails
      })
  }

  afterAppInit = () => {
    setTimeout(() => {
      this.props.handleNavigation({ type: 'REPLACE', route: { key: 'home' } });
    }, AFTER_INIT_DELAY);
  }

  render() {
    return (
      <SplashSceen dataReady={this.state.dataReady} />
    );
  }
}