diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f358fd..475002de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -## [9.1.0](https://github.com/imgix/react-imgix/compare/v9.0.4...v9.1.0) (2021-04-23) +## [9.1.0](https://github.com/imgix/react-imgix/compare/v9.0.4...v9.1.0) (2021-04-23) ### Features diff --git a/src/react-imgix-bg.jsx b/src/react-imgix-bg.jsx index 38fd2a64..71f5e90a 100644 --- a/src/react-imgix-bg.jsx +++ b/src/react-imgix-bg.jsx @@ -5,6 +5,7 @@ import constructUrl from "./constructUrl"; import extractQueryParams from "./extractQueryParams"; import findClosest from "./findClosest"; import targetWidths from "./targetWidths"; +import { shallowEqual } from "./common"; const noop = () => {}; @@ -13,107 +14,153 @@ const findNearestWidth = (actualWidth) => const toFixed = (dp, value) => +value.toFixed(dp); -const BackgroundImpl = (props) => { - const { - measureRef, - measure, - contentRect, - imgixParams = {}, - onLoad, - disableLibraryParam, - src, - children, - className = "", - } = props; - const { w: forcedWidth, h: forcedHeight } = imgixParams; - const hasDOMDimensions = - contentRect.bounds.width != null && contentRect.bounds.height != null; - const htmlAttributes = props.htmlAttributes || {}; - const dpr = toFixed(2, imgixParams.dpr || global.devicePixelRatio || 1); - const ref = htmlAttributes.ref; - const onRef = (el) => { - measureRef(el); - if (typeof ref === "function") { - ref(el); - } - }; +class BackgroundImpl extends React.Component { + constructor(props) { + super(props); + } + + shouldComponentUpdate(nextProps) { + const contentRect = this.props.contentRect; + const bounds = contentRect.bounds; + const { width: prevWidth, height: prevHeight } = bounds; - const { width, height } = (() => { - const bothWidthAndHeightPassed = - forcedWidth != null && forcedHeight != null; + const nextContentRect = nextProps.contentRect; + const nextBounds = nextContentRect.bounds; + const { width: nextWidth, height: nextHeight } = nextBounds; - if (bothWidthAndHeightPassed) { - return { width: forcedWidth, height: forcedHeight }; + // If neither of the previous nor next dimensions are present, + // re-render. + if (!nextWidth || !nextHeight || !prevWidth || !prevHeight) { + return true; } - if (!hasDOMDimensions) { - return { width: undefined, height: undefined }; + // The component has been rendered at least twice by this point + // and both the previous and next dimensions should be defined. + // Only update if the nextWidth is greater than the prevWidth. + if (prevWidth && nextWidth && nextWidth > prevWidth) { + return true; } - const ar = contentRect.bounds.width / contentRect.bounds.height; - - const neitherWidthNorHeightPassed = - forcedWidth == null && forcedHeight == null; - if (neitherWidthNorHeightPassed) { - const width = findNearestWidth(contentRect.bounds.width); - const height = Math.ceil(width / ar); - return { width, height }; + + // Similarly, only update if the next height is greater than + // the previous height. + if (prevHeight && nextHeight && nextHeight > prevHeight) { + return true; } - if (forcedWidth != null) { - const height = Math.ceil(forcedWidth / ar); - return { width: forcedWidth, height }; - } else if (forcedHeight != null) { - const width = Math.ceil(forcedHeight * ar); - return { width, height: forcedHeight }; + + // If we made it here, we need to check if the "top-level" + // props have changed (e.g. disableLibraryParam). + const shallowPropsEqual = shallowEqual(this.props, nextProps); + + // We also need to check the imgixParams. + const shallowParamsEqual = shallowEqual( + this.props.imgixParams, + nextProps.imgixParams + ); + + return shallowPropsEqual && shallowParamsEqual; + } + + render() { + const { + measureRef, + contentRect, + imgixParams = {}, + onLoad, + disableLibraryParam, + src, + children, + className = "", + } = this.props; + const { w: forcedWidth, h: forcedHeight } = imgixParams; + const hasDOMDimensions = + contentRect.bounds.width != null && contentRect.bounds.height != null; + const htmlAttributes = this.props.htmlAttributes || {}; + const dpr = toFixed(2, imgixParams.dpr || global.devicePixelRatio || 1); + const ref = htmlAttributes.ref; + const onRef = (el) => { + measureRef(el); + if (typeof ref === "function") { + ref(el); + } + }; + + const { width, height } = (() => { + const bothWidthAndHeightPassed = + forcedWidth != null && forcedHeight != null; + + if (bothWidthAndHeightPassed) { + return { width: forcedWidth, height: forcedHeight }; + } + + if (!hasDOMDimensions) { + return { width: undefined, height: undefined }; + } + const ar = contentRect.bounds.width / contentRect.bounds.height; + + const neitherWidthNorHeightPassed = + forcedWidth == null && forcedHeight == null; + if (neitherWidthNorHeightPassed) { + const width = findNearestWidth(contentRect.bounds.width); + const height = Math.ceil(width / ar); + return { width, height }; + } + if (forcedWidth != null) { + const height = Math.ceil(forcedWidth / ar); + return { width: forcedWidth, height }; + } else if (forcedHeight != null) { + const width = Math.ceil(forcedHeight * ar); + return { width, height: forcedHeight }; + } + })(); + const isReady = width != null && height != null; + + const commonProps = { + ...htmlAttributes, + }; + + if (!isReady) { + return ( +