big boy
big boy

Reputation: 395

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>
    )
}

Upvotes: 0

Views: 1023

Answers (3)

big boy
big boy

Reputation: 395

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...

Upvotes: 0

Vincent La
Vincent La

Reputation: 504

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.

Upvotes: 1

user11847513
user11847513

Reputation:

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.

Upvotes: 0

Related Questions