状态更改不会导致功能组件中的重新渲染

时间:2020-09-22 06:38:10

标签: reactjs react-native react-hooks

在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>
            );

        };        
        
};

2 个答案:

答案 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>
)