Where to perform startup tasks?

When using the AppLoading component, is there a standard way of loading some startup activities? I see the example in the doc here: https://docs.expo.io/versions/latest/sdk/app-loading.html#app-loading

I was hoping to perform startup tasks using redux/saga, but if the AppLoading component is the first component loaded then that means the redux store won’t be available.

Wondering how others are doing this with redux, or if at all. Thanks.

1 Like

Well it is a React component, so you could look at componentWillMount?

Thanks Edgar, that’s what I’m doing now, but I was hoping to do something like:

  1. Use AppLoading component to load from /assets, fonts, etc
  2. Keep the AppLoading component on the screen, but now start using react-redux and to do some other actions
  3. Move on to the App’s screen

Initially I was hoping to wrap the component with , but according to the Expo doc AppLoading has to be the first component so that idea is out.

Just wondering how others are handling this sort of thing.

@robotron Right now I am asking myself the same than you. How did you do it finally?

It’s been a while but I ended up doing something like this:

state = {
    expoReady: false,
  };

  componentWillMount() {
    this.loadResources();
  }

  async loadResources() {

    await Expo.Font.loadAsync({
        // do some loading
    });

    this.setState({ expoReady: true });
  }

  render() {
    if (!this.state.expoReady) {
      return (
        <AppLoading />
      );
    }

    return (
      <Provider store={store}>
        <View style={styles.container}>
          <ReduxNavigation />
        </View>
      </Provider>
    );
  }
}

I figured out a way of doing this so you can have all your startup tasks in a saga, and still use Expo’s <AppLoading> component. The tricky thing is having to subscribe to store updates manually, as <AppLoading> requires being the first and only component rendered.

// We want to display the native splash screen, until all the required assets have loaded.
// Expo has a component, <AppLoading>, for this.
// All the assets are loaded by the main saga. After they've loaded, the ASSETS_LOADED action
// is dispatched, updating the haveAssetsUpdated flag in the store.
// However, Expo requires that <AppLoading> is the first and only component loaded, therefore
// we can't switch between <AppLoading> and the rest of the App within <Provider>, as then
// <AppLoading> isn't the first or only component, so it won't work.
// Therefore we need another way of subscribing <App> to the store.
// We can do this by subscribing to a listener for store changes, using redux's store.subscribe().
// Inside the listener, if we detect that the haveAssetsLoaded flag has been set to true, then
// we mirror this in App's own state, so that the App component rerenders.
// As store.subscribe fires *every time* the store updates, we want to unsubscribe from this
// listener ASAP, i.e. as soon as haveAssetsLoaded has been set to true. This avoids the
// unnecessary performance hit of having the listener firing through the rest of the app's
// lifecycle.

class App extends Component {
  state = {
    haveAssetsLoaded: false,
  };

  constructor(props) {
    super(props);
    const unsubscribeFromStore = store.subscribe(() => {
      if (store.getState().haveAssetsLoaded) {
        // as soon as the main saga has loaded all assets, it sets haveAssetsLoaded: true in
        // the store. We detect this here and, in turn, set haveAssetsLoaded: true in <App>'s
        // state, causing App to rerender. Then unsubscribe this listener.
        this.setState({ haveAssetsLoaded: true });
        unsubscribeFromStore();
      }
    });
  }

  render() {
    const { haveAssetsLoaded } = this.state;
    if (!haveAssetsLoaded) {
      return (
        <AppLoading />
      );
    }

    return (
      <Provider store={store}>
          <AppContainer />
      </Provider>
    );
  }
}

Having a similar circumstance. In my case, I want to download some initial assets from the server (although I’m looking at assetBundlePatterns in app.json) and once those assets have downloaded using Expo.FileSystem I then also want to store the contents of this newly downloaded data into the Redux Store. I want to utilize the AppLoading directive so I had an idea where this data would be stored within a singleton until all of the Promises have completed and then using another React.Component to hydrate the state from this singleton. I haven’t tried this yet but I’ll be doing so later today…Feels wrong though but I can’t think of a better way to modularize this. Will report back.

1 Like

Hey @adriaanbalt, I’m facing the same issue and wondering how you resolve using <AppLoading /> integrating it with a Redux store.

1 Like

Hi @pskill I do not use <AppLoading>. I have a custom <Loader> and positioned it within the Redux Provider like this:

	render() {
		return (
			<Provider store={store}>
				<View
					style={{
						marginTop: 0,
						width: "100%",
						height: "100%",
						flex: 1,
					}}>
					{this.state.isLoaderVisible && (
						<Animated.View
							style={[
								styles.loaderContainer,
								{
									opacity: this.state._loaderOpacity,
								},
							]}>
							<Loader
								onFinish={this._handleFinishLoading}
								onError={this._handleLoadingError}
							/>
						</Animated.View>
					)}
					<AppNavigator />
					<OTALoader />
				</View>
			</Provider>
		);
	}

	_handleLoadingError = (error) => {
		throw new Error(error);
	};

	_handleFinishLoading = () => {
		this.setState({ isLoadingComplete: true });
	};