From 2e822c068dda92c18bb9e5749c9060386307c6f5 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Mon, 15 Aug 2016 15:00:47 -0700 Subject: [PATCH] [fix] Image render thrashing This patch removes several avoidable uses of `setState`, only re-rendering the `Image` when necessary. It also fixes a style prop warning for `resizeMode`, which was not being removed in development due to the use of `Object.freeze` when styles are registered. Close #116 --- examples/Image/ImageExample.js | 2 ++ src/components/Image/index.js | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/examples/Image/ImageExample.js b/examples/Image/ImageExample.js index 041d8346..1223fd5f 100644 --- a/examples/Image/ImageExample.js +++ b/examples/Image/ImageExample.js @@ -406,6 +406,7 @@ const examples = [ ); }, }, + /* { title: 'Tint Color', description: 'The `tintColor` style prop changes all the non-alpha ' + @@ -456,6 +457,7 @@ const examples = [ ); }, }, + */ { title: 'Resize Mode', description: 'The `resizeMode` style prop controls how the image is ' + diff --git a/src/components/Image/index.js b/src/components/Image/index.js index dd34ca0f..3d7b4128 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -51,17 +51,18 @@ class Image extends Component { constructor(props, context) { super(props, context) const uri = resolveAssetSource(props.source) - this.state = { status: uri ? STATUS_PENDING : STATUS_IDLE } + this._imageState = uri ? STATUS_PENDING : STATUS_IDLE + this.state = { isLoaded: false } } componentDidMount() { - if (this.state.status === STATUS_PENDING) { + if (this._imageState === STATUS_PENDING) { this._createImageLoader() } } componentDidUpdate() { - if (this.state.status === STATUS_PENDING && !this.image) { + if (this._imageState === STATUS_PENDING && !this.image) { this._createImageLoader() } } @@ -69,9 +70,7 @@ class Image extends Component { componentWillReceiveProps(nextProps) { const nextUri = resolveAssetSource(nextProps.source) if (resolveAssetSource(this.props.source) !== nextUri) { - this.setState({ - status: nextUri ? STATUS_PENDING : STATUS_IDLE - }) + this._updateImageState(nextUri ? STATUS_PENDING : STATUS_IDLE) } } @@ -80,6 +79,7 @@ class Image extends Component { } render() { + const { isLoaded } = this.state const { accessibilityLabel, accessible, @@ -90,13 +90,13 @@ class Image extends Component { testID } = this.props - const isLoaded = this.state.status === STATUS_LOADED const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source) const backgroundImage = displayImage ? `url("${displayImage}")` : null - const style = StyleSheet.flatten(this.props.style) + let style = StyleSheet.flatten(this.props.style) const resizeMode = this.props.resizeMode || style.resizeMode || ImageResizeMode.cover - // remove resizeMode style, as it is not supported by View + // remove 'resizeMode' style, as it is not supported by View (N.B. styles are frozen in dev) + style = process.env.NODE_ENV !== 'production' ? { ...style } : style delete style.resizeMode /** @@ -153,7 +153,7 @@ class Image extends Component { const event = { nativeEvent: e } this._destroyImageLoader() - this.setState({ status: STATUS_ERRORED }) + this._updateImageState(STATUS_ERRORED) this._onLoadEnd() if (onError) onError(event) } @@ -163,7 +163,7 @@ class Image extends Component { const event = { nativeEvent: e } this._destroyImageLoader() - this.setState({ status: STATUS_LOADED }) + this._updateImageState(STATUS_LOADED) if (onLoad) onLoad(event) this._onLoadEnd() } @@ -175,9 +175,17 @@ class Image extends Component { _onLoadStart() { const { onLoadStart } = this.props - this.setState({ status: STATUS_LOADING }) + this._updateImageState(STATUS_LOADING) if (onLoadStart) onLoadStart() } + + _updateImageState(status) { + this._imageState = status + const isLoaded = this._imageState === STATUS_LOADED + if (isLoaded !== this.state.isLoaded) { + this.setState({ isLoaded }) + } + } } applyNativeMethods(Image)