skip to Main Content

I have a code where I output images dynamically. And in the state I have an array with objects {url: null, name: null}.

When I load an image, I get the image name fine, but not url. And i can’t understand why. Here is the complete code:

const [UploadLogos, setUploadLogos] = useState([
  { url: null, name: null}
])

const setParam = (Param, newParam, i) => {
  setUploadLogos([
    ...UploadLogos.slice(0, i),
    { ...UploadLogos[i], [Param]: newParam },
    ...UploadLogos.slice(i + 1),
  ])
}

{
  UploadLogos.map((Logo, i) => (
    <ImageUpload
      key={i}
      Image={Logo.url}
      setImage={(prev) => setParam("url", prev, i)}
      Name={Logo.name}
      setName={(prev) => setParam("name", prev, i)}
    />
  ))
}

ImageUpload.jsx

export function ImageUpload({Image, setImage, Name, setName}) {
    return (
      <>
        <input 
            type="file" accept="image/png" onChange={event => { 
                const ImageURL = URL.createObjectURL(event.target.files[0])
                const ImageName = event.target.files[0]['name']

                setImage(ImageURL)                  
                setName(ImageName)
                
                console.log(ImageURL) ===> blob:http://localhost......
                console.log(Image) ===> null
                
                console.log(ImageName) ===> simpleImage.png
                console.log(Name) ===> simpleImage.png
              
            }} 
        />
      </>   
    )
}

In the console in the component I showed what I was getting. And Image is always null, although, I put it in setImage(ImageURL).

What is the error?

4

Answers


  1. Maybe share the console.log result of the entire UploadLogos array from a useEffect and see if that’s right.

    One thing about using an array index as the key when you map is that it doesn’t track changes in the array when you do stuff like re-order or slice etc. because the index doesn’t change key={i}

    Here’s why:
    error: Do not use Array index in keys

    Try using key={Logo.url} and see if that works

    Login or Signup to reply.
  2. when you call setImage(), the state is not updated immidiately. so, when you reach console.log(Image), it is still printing the previous value and not the new value.

    you can see if the correct value is set for image, in a useEffect like this:

    useEffect(() => {
        console.log(uploadLogos);
    }, [UploadLogos]);
    
    Login or Signup to reply.
  3. The error you seeing is because you are setting a same state variable without using functional update feature of useState hook.

    The code that causes the issue:

    const setParam = (Param, newParam, i) => {
      setUploadLogos([
        ...UploadLogos.slice(0, i),
        { ...UploadLogos[i], [Param]: newParam },
        ...UploadLogos.slice(i + 1),
      ])
    }
    

    And

    setImage(ImageURL)                  
    setName(ImageName)
    

    The core of the issue is that react’s setter from useState hook is not updating the variable itself immediately, updates will be available on next render only. And because of that – setImage results are completely ignored due to setName overwrites the data that was set previously, because UploadLogos variable does not have any data set by setImage yet, in the current render.

    The fix:

    const setParam = (Param, newParam, i) => {
      setUploadLogos((curr) => [
        ...curr.slice(0, i),
        {...curr[i], [Param]: newParam},
        ...curr.slice(i + 1),
      ])
    }
    
    Login or Signup to reply.
  4. Your code is good but here is one issue while setting the state.

    Here your state is not getting preserved that’s why you were getting it as null as name is overwriting the state.

    const setParam = (Param, newParam, i) => {
      setUploadLogos([
        ...UploadLogos.slice(0, i),
        { ...UploadLogos[i], [Param]: newParam },
        ...UploadLogos.slice(i + 1),
      ])
    }
    

    SO this is how you can preserve the state with minor change:-

    const setParam = (Param, newParam, i) => {
       setUploadLogos((prev) => [
         ...UploadLogos.slice(0, i),
         { ...prev[i], [Param]: newParam },
         ...UploadLogos.slice(i + 1),
       ]);
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search