I will share my own solution based only on CSS manipulation, which in my opinion is easy to understand, and the code is pretty clean. The solution is a little similar to other answers, but doesn't require absolute position of any component, or creating any additional components.
The idea is to switch between showing an <Image> and <ActivityIndicator>, based on some state variable (isImageLoaded in the snippet below).
<View>
    <Image source={...}
        onLoad={ () => this.setState({ isImageLoaded: true }) }
        style={[styles.image, { display: (this.state.isImageLoaded ? 'flex' : 'none') }]}
    />
    <ActivityIndicator
        style={{ display: (this.state.isImageLoaded ? 'none' : 'flex') }}
    />
</View>
Also you should set image size using flex property (otherwise image will be invisible):
const styles = StyleSheet.create({
  image: {
    flex: 1,
  }
});
Note that you don't have to initiate the isImageLoaded variable to false in the constructor, because it will have undefined value and the if conditions will act as expected.