简体   繁体   中英

React, component not re-rendering after change in an array state (not the same as others)

I'm trying to make a page that gets picture from a server and once all pictures are downloaded display them, but for some reason the page doesn't re-render when I update the state.

I've seen the other answers to this question that you have to pass a fresh array to the setImages function and not an updated version of the previous array, I'm doing that but it still doesn't work.

(the interesting thing is that if I put a console.log in an useEffect it does log the text when the array is re-rendered, but the page does not show the updated information)

If anyone can help out would be greatly appreciated!

Here is my code.

 export function Profile() {
    const user = JSON.parse(window.localStorage.getItem("user"));
    const [imgs, setImages] = useState([]);
    const [num, setNum] = useState(0);
    const [finish, setFinish] = useState(false);

    const getImages = async () => {
        if (finish) return;
        let imgarr = [];
        let temp = num;
        let filename = "";
        let local = false;
        while(temp < num+30) {
            fetch("/get-my-images?id=" + user.id + "&logged=" + user.loggonToken + "&num=" + temp)
            .then(response => {
                if(response.status !== 200) {
                    setFinish(true);
                    temp = num+30;
                    local = true;
                }
                filename = response.headers.get("File-Name");
                return response.blob()
            })
            .then(function(imageBlob) {
                if(local) return;
                const imageObjectURL = URL.createObjectURL(imageBlob);
                imgarr[temp - num] = <img name={filename} alt="shot" className="img" src={imageObjectURL}  key={temp} />
                temp++;
            });
        }
        setNum(temp)
        setImages(prev => [...prev, ...imgarr]);
    }

    async function handleClick() {
        await getImages();
    }

    return (
        <div>
            <div className="img-container">
                {imgs.map(i => {
                    return (
                        i.props.name && <div className="img-card">
                            <div className="img-tag-container" onClick={(e) => handleView(i.props.name)}>{i}</div>
                            
                            <div className="img-info">
                                <h3 className="title" onClick={() => handleView(i.props.name)}>{i.props.name.substr(i.props.name.lastIndexOf("\\")+1)}<span>{i.props.isFlagged ? "Flagged" : ""}</span></h3>
                            </div>
                        </div>
                    )
                })}
            </div>
            <div className="btn-container"><button className="load-btn" disabled={finish} onClick={handleClick}>{imgs.length === 0 ? "Load Images" : "Load More"}</button></div>
        </div>
    )
}

I think your method of creating the new array is correct. You are passing an updater callback to the useState() updater function which returns a concatenation of the previous images and the new images, which should return a fresh array.

When using collection-based state variables, I highly recommend setting the key property of rendered children. Have you tried assigning a unique key to <div className="img-card"> ?. It appears that i.props.name is unique enough to work as a key.

Keys are how React associates individual items in a collection to their corresponding rendered DOM elements. They are especially important if you modify that collection. Whenever there's an issue with rendering collections, I always make sure the keys are valid and unique. Even if adding a key doesn't fix your issue, I would still highly recommend keeping it for performance reasons.

It is related to Array characteristics of javascript. And the reason of the console log is related with console log print moment. So it should be shown later updated for you.

There are several approaches.

const getImages = async () => {
      ... ...
        setNum(temp)
        const newImage = [...prev, ...imgarr];
        setImages(prev => newImage);
    }
const getImages = async () => {
      ... ...
        setNum(temp)
        setImages(prev => JOSN.parse(JSON.object([...prev, ...imgarr]);
    }
const getImages = async () => {
      ... ...
        setNum(temp)
        setImages(prev => [...prev, ...imgarr].slice(0));
    }

Maybe it could work. Hope it will be helpful for you.

Ok the problem for me was the server was not sending a proper filename header so it was always null so the condition i.props.name was never true... lol sorry for the confusion.

So the moral of this story is, always make sure that it's not something else in your code that causes the bad behavior before starting to look for other solutions...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM