RN的Image组件必须设置样式的width
和height
才能正常显示,但我们通常都不能把高度写死,比如像宽度固定高度自适应的需求。
Image组件有个getSize方法可以帮助我们解决这个问题。(针对通过网络获取的图片)
但这个方法是异步的,每次在副作用中(请求数据)得到了图片数据,再异步处理图片的高度容易产生内存泄露等bug,代码逻辑也显得复杂和冗余。所以单独抽取一个图片组件,直接把图片地址直接传给它,处理宽高这件事就由这个组件来处理。
ScaledImage组件
import React,{ useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Image, Dimensions } from 'react-native'
const { width: screenWidth, height: screenHeight } = Dimensions.get('window')
const ScaledImage = (props) => {
const [source, setSource] = useState({uri: props.uri})
const styles = props.style ? props.style : {} // 定义其他样式
// 为了区分宽度/高度必要性,width 和 height prop 我们单独传,若有其他样式要求可传给styleProp
useEffect(() => {
Image.getSize(props.uri,(width, height) => {
if (props.width && !props.height) { // 图片宽度固定,高度自适应
setSource({uri: props.uri, width: props.width, height: height * (props.width / width)});
} else if (!props.width && props.height) { // 图片高度固定,宽度自适应
setSource({uri: props.uri, width: width * (props.height / height), height: props.height});
} else if(!props.width && !props.height){ // 图片宽高都没有传,那就是宽度占满容器,高度自适应
setSource({uri: props.uri, width: '100%', height: Math.floor(screenWidth/width*height)});
} else { // 图片宽高都传了
setSource({uri: props.uri, width: props.width, height: props.height});
}
});
},[])
const imageStyle = {
width: source.width,
height: source.height,
...styles,
}
return <Image source={{uri:source.uri}} style={imageStyle} />
}
ScaledImage.propTypes = {
uri: PropTypes.string.isRequired,
width: PropTypes.number,
height: PropTypes.number
}
export default ScaledImage
使用组件示例,需要的地方都可以用ScaledImage
组件愉快地取代Image
了
import React, { useEffect, useState, useRef } from 'react'
import ScaledImage from '@components/core/ScaledImage'
import { apiFun } from '@api/fish'
const MuseumDetail = ({ navigation, route }) => {
const apiUrl = global.baseUrl
const [detailInfo, setDetailInfo] = useState(null)
const { id } = route.params
const count = useRef(0)
useEffect(() => {
const currentCount = count.current;
const getDetail = async () => {
const result = await apiFun(id)
if(count.current !== currentCount) return
if(result.code === 200){
let detail = result.data
// ... 一些其他对数据的处理
setDetailInfo(detail)
}
}
getDetail()
return () => { count.current += 1 } // 竞态处理
},[route.params.id])
return (
detailInfo && <ScaledImage
uri={typeof detailInfo.photoSrc === 'string' ? `${apiUrl}${detailInfo.photoSrc}`: apiUrl + detailInfo.photoSrc[0].src}
width={styles.detailImg.width}
style={{borderRadius: 40, marginLeft: 'auto'}}
/>
)
}
export default MuseumDetail