在React Native 0.63.2应用程序中,有一个功能组件DisplayImages
用于显示上传的图像。在状态Imgs
中更改图像顺序后,DisplayImages
会重新呈现。这是代码:
import DisplayImages from "./DisplayImages";
export default function Itie({navigation}) { //<<==this is the parent component which calls child DisplayImages
const [imgs, setImgs] = useState(); //<<==image array to display
return(
<MyAccordion
title={"Image"}
absPosition={false}
screenSize={{width:screen_width, height:((imgs && imgs.length>9) ? screen_width+(screen_width/3)*(Math.ceil(((imgs.length-9)%3)/3)):screen_width)}}
ref= {refImg}
>
<DisplayImages pics={imgs} deleteImage={deleteImage} updateImage={(new_img)=> {setImgs(new_img);console.log("in updateImage", new_img.map((it) => it.fileName))}} swipeImage={(indx) => swipeImage(indx)}/> //<<==here is the child DisplayImages. Imgs is a useState() which is an array of images. Its order will be changed if user drag and reorder the array.
</MyAccordion>
)
这是console.output,用于显示图像Imgs
的顺序已更改:
[Mon Sep 21 2020 23:17:49.624] LOG pics before move : ["26689.1 (5-18-19) image 3.jpg", "4.jpg", "1.jpg"]
[Mon Sep 21 2020 23:17:49.626] LOG pics after move : ["1.jpg", "26689.1 (5-18-19) image 3.jpg", "4.jpg"]
[Mon Sep 21 2020 23:17:49.626] LOG in updateImage ["1.jpg", "26689.1 (5-18-19) image 3.jpg", "4.jpg"] //<<==updateImage in DisplayImages calls setImgs to update the Imgs
随着状态Imgs
的更新,组件DisplayImages
有望重新呈现。但是,事实并非如此。为什么更新状态Imgs
不会导致重新渲染?
更新:DisplayImages
组件。 DisplayImages
用于以正方形网格显示图像,该图像的宽度可能随图像总数而变化。手势处理用于处理拖动和排序图像。该代码在dev中,可能很难阅读。
import React, { useRef, useMemo, useState, useContext} from 'react';
import {View, FlatList, Image, StyleSheet, ScrollView, TextInput, Platform, TouchableOpacity, SafeAreaView, Dimensions, TouchableWithoutFeedback } from 'react-native';
import { Col, Row, Grid } from 'react-native-easy-grid';
import { PanGestureHandler, PinchGestureHandler, State, RotationGestureHandler } from "react-native-gesture-handler";
import FastImage from "react-native-fast-image";
import Animated, { useValue } from "react-native-reanimated";
const {Value,event,cond,block,set,eq,add,abs,divide,multiply,not,clockRunning,and,or,greaterOrEq,startClock,stopClock,greaterThan,lessThan,call,Clock} = Animated;
import { propsContext} from "../app/GlobalContext";
//import {DeleteButton, GridImage, ModalImage, ModalImageAndroidWrap} from "./viewComponent";
const {width, height} = Dimensions.get("window");
//display images in Col based on number of images
export default DisplayImages = ({pics, deleteImage, updateImage, swipeImage}) => {
//params
const propsVal = useContext(propsContext);
const device_id=propsVal.device_id, screen_width=width, screen_ht=height;
//const [tf, setTf] = useState(false);
if (!pics || pics===[] || pics==={}) return null;
var len = pics.length, full=0.98, half=0.49, oneThird=0.32;
console.log("# of images : ", len);
//check if there is field domain in pics passed in
const picPath = (item) => {
if (item.domain) {
return (item.domain + item.path);
} else {
return item.path;
};
};
const move = (from, to) => {
//
console.log(" pics before move : ", pics.map((it) => it.fileName));
var target = pics[from];
var increment = to < from ? -1 : 0;
pics.splice(from, 1);
if (Math.abs(to-from)==1) {
pics.splice(to,0, target);
} else {
pics.splice(to+increment<=0 ? 0:to+increment,0, target);
};
console.log("pics after move : ", pics.map((it) => it.fileName));
updateImage(pics);
}
function DisplayImg ({img_source, width, ht, index, handleSwipe, modalWidth, modalHt, dataLen, sortFn=null}) {
const aniIndex= new Value(index);
const dragX = (useValue(0));
const dragY = (useValue(0));
const offsetX = new Value(0);
const offsetY = new Value(0);
var transX = useValue(0);
var transY = useValue(0);
const state = useValue(-1);
const scale = useValue(1);
const rotate = useValue(0);
const offsetZ = useValue(1);
var gridPerRow;
console.log("image width : ", width);
console.log("image ht : ", ht);
if (dataLen <= 4 && dataLen > 1) {
gridPerRow = 2;
} else if (dataLen > 4) {
gridPerRow = 3;
} else {
gridPerRow = 1;
};
const aniGridPR = new Value(gridPerRow);
function onDrop ([x, y, indx, gridPR]) {
console.log("x onDrop ", x);
console.log("y onDrop ", y);
console.log("index : ", index);
console.log("Grid PR : ", gridPR);
var jump_y=0, jump_x=0, new_index, percentage;
//jump_row
jump_y = y>0 ? Math.floor(y/ht) : -Math.floor(-y/ht);
percentage = Math.abs((y%ht)/ht);
if (percentage >= 0.3) (y>0 ? jump_y++ : jump_y--); //count if Y overlap 30% or more
console.log("Y jump : ", jump_y);
//jump col
jump_x = x>0 ? Math.floor(x/width) : -Math.floor(-x/width);
//console.log("jump X : ", jump_x);
percentage = Math.abs((x%width)/width);
//console.log("col percentage : ", percentage);
if (percentage >= 0.3) (x>0 ? jump_x++ : jump_x--); //count if X overlap 30% or more
console.log("X jump : ", jump_x);
new_index = indx + jump_y*gridPR + jump_x
console.log("new index : ", new_index);
if (new_index != indx) {
sortFn(indx, new_index);
};
};
if (img_source && img_source!==[] && img_source!=={}) {
console.log("ani code");
const addX = add(offsetX, dragX);
const addY = add(offsetY, dragY);
transX = cond(eq(state, State.ACTIVE), addX);
transY = cond(eq(state, State.ACTIVE), addY,
cond(eq(state, State.END),
cond(or(greaterOrEq(abs(divide(dragX,new Value(width))), new Value(0.3)), greaterOrEq(abs(divide(dragY,new Value(ht))), new Value(0.3))),
call([addX, addY, aniIndex, aniGridPR], onDrop))
)
);
//console.log("2", transY);
const handleGesture = event([
{
nativeEvent: {
translationX: dragX,
translationY: dragY,
state,
},
},
]);
let aniStyle = {
transform:[
{ translateX : transX },
{ translateY : transY },
]
};
let scaleStyle = {
transform:[
{ perspective: 500 },
{
scale : scale
}
]
};
return (
<>
<PanGestureHandler
onGestureEvent={handleGesture}
onHandlerStateChange={handleGesture}
minPointers={1}
maxPointers={1}>
<Animated.View style={[aniStyle ]}>
<TouchableOpacity onLongPress={() => deleteImage(index)} onPress={()=>handleSwipe()} >
<Animated.View style={[styles.wrapper]}>
<FastImage
source={{uri:img_source}}
resizeMode={FastImage.resizeMode.cover}
style={[{
width:width,
height:ht,
verticalAlign:0,
paddingTop:0
}]}
/>
</Animated.View>
</TouchableOpacity>
</Animated.View>
</PanGestureHandler>
</>
);
} else {
return null;
};
};
//if (len > 0) setImgAccordOpen(true);
switch(len) {
case 0:
return null;
case 1:
return (
<Grid style={{position:"absolute", paddingTop:0,paddingLeft:0}}>
<Row style={styles.row} key={pics[0].fileName}>
<DisplayImg
img_source={picPath(pics[0])}
width={screen_width*full}
ht={screen_width*full}
index= {0}
modalWidth={screen_width}
modalHt= {pics[0].height*(screen_width/pics[0].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
</Grid>
);
case 2:
case 3:
case 4:
return (
<Grid style={{position:"absolute", paddingTop:0,paddingLeft:0}}>
{pics.map((item, index) => {
if (index%2===0) {
if (pics[index+1]) {
return (
<Row style={styles.row} key={pics[index].fileName+pics[index+1].fileName}>
<DisplayImg
img_source={picPath(pics[index])}
width={screen_width*half}
ht={screen_width*half}
index= {index}
modalWidth={screen_width}
modalHt= {pics[index].height*(screen_width/pics[index].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
<DisplayImg
img_source={picPath(pics[index+1])}
width={screen_width*half}
ht={screen_width*half}
index= {index+1}
modalWidth={screen_width}
modalHt= {pics[index+1].height*(screen_width/pics[index+1].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
)} else {
return (
<Row style={styles.row} key={pics[index].fileName}>
<DisplayImg
img_source={picPath(pics[index])}
width={screen_width*half}
ht={screen_width*half}
index= {index}
modalWidth={screen_width}
modalHt= {pics[index].height*(screen_width/pics[index].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
)};
}
})}
</Grid>
);
default:
return (
<Grid style={{position:"absolute", paddingTop:0,paddingLeft:0}}>
{pics.map((item, index) => {
if (index%3===0) {
if (pics[index+2]) {
return (
<Row style={styles.row} key={pics[index].fileName+pics[index+1].fileName+pics[index+2].fileName}>
<DisplayImg
img_source={picPath(pics[index])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index}
modalWidth={screen_width}
modalHt= {pics[index].height*(screen_width/pics[index].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
<DisplayImg
img_source={picPath(pics[index+1])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index+1}
modalWidth={screen_width}
modalHt= {pics[index+1].height*(screen_width/pics[index+1].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
<DisplayImg
img_source={picPath(pics[index+2])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index+2}
modalWidth={screen_width}
modalHt= {pics[index+2].height*(screen_width/pics[index+2].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
)} else if (pics[index+1]) {
return (
<Row style={styles.row} key={pics[index].fileName+pics[index+1].fileName}>
<DisplayImg
img_source={picPath(pics[index])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index}
modalWidth={screen_width}
modalHt= {pics[index].height*(screen_width/pics[index].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
<DisplayImg
img_source={picPath(pics[index+1])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index+1}
modalWidth={screen_width}
modalHt= {pics[index+1].height*(screen_width/pics[index+1].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
)} else if (!pics[index+1]) {
return (
<Row style={styles.row} key={pics[index].fileName}>
<DisplayImg
img_source={picPath(pics[index])}
width={screen_width*oneThird}
ht={screen_width*oneThird}
index= {index}
modalWidth={screen_width}
modalHt= {pics[index].height*(screen_width/pics[index].width)}
dataLen={len}
sortFn={move}
handleSwipe={swipeImage}
/>
</Row>
)} else {
return null;
};
}
})}
</Grid>
);
};
};
答案 0 :(得分:2)
React.memo函数,可用于确定组件是否应在挂钩中呈现 如果函数返回true,则组件将不会在更改该属性时重新渲染,相反,当返回值为false时,它将更新
function Itie({prop1, prop2}) {
return(
..
)
}
React.memo(Itie, (props, nextProps)=> {
if(props.prop1 === nextProps.prop1) {
// don't re-render/update
return true
}
})
答案 1 :(得分:0)
没有渲染的问题可能是由2个数组完全相同的元素引起的,但是不同的顺序在Javascript中被视为相同。为了强制重新渲染,将布尔状态flip
添加到组件Itie
。每当图像阵列发生顺序更改时,flip
都会更改其值,从而触发重新渲染。以下是相关代码:
export default function Itie({navigation}) {
const [flip, setFlip] = useState(false);
//do something
return (
<MyAccordion
title={"Image"}
absPosition={false}
screenSize={{width:screen_width, height:((imgs && imgs.length>9) ? screen_width+(screen_width/3)*(Math.ceil(((imgs.length-9)%3)/3)):screen_width)}}
ref= {refImg}
>
<DisplayImages pics={imgs} deleteImage={deleteImage} flip={flip} updateImage={(new_img) => {setImgs(new_img);setFlip(!flip);}} swipeImage={swipeImage}/>
</MyAccordion>
)