Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The component is not updated when a new state is changed using a callback from useState #14910

Closed
Finesse opened this issue Feb 21, 2019 · 3 comments

Comments

@Finesse
Copy link

Finesse commented Feb 21, 2019

This is a bug. I've spent a lot of time trying to localize the bug and have ended with the following minimal example:

import {useState} from React;

function App() {
  const [imageURL, setImageURL] = useState('https://picsum.photos/1600/1200');
  const [meaninglessValue, setMeaninglessValue] = useState(0);
  
  return (
    <div>
      <p>
        <button onClick={() => setImageURL('https://picsum.photos/1600/1200')}>Set picture 1</button>
        <button onClick={() => setImageURL('https://picsum.photos/1500/1300')}>Set picture 2</button>
        <button onClick={() => setMeaninglessValue(meaninglessValue + 1)}>Change another image prop</button>
      </p>
      <DelayedImage src={imageURL} unused={meaninglessValue} />
    </div>
  );
}

function DelayedImage({src}) {
  const [previousSrc, setPreviousSrc] = useState();
  const [isLoaded, setIsLoaded] = useState(false);

  console.log('Image is rendered with', {previousSrc, src, isLoaded});
  
  if (src !== previousSrc) {
    setPreviousSrc(src);
    setIsLoaded(false);
    console.log('Image unloaded');
  }
  
  const handleLoad = () => {
    setIsLoaded(true);
    console.log('Image loaded');
  };
  
  return (
    <img
      src={src}
      alt=""
      onLoad={handleLoad}
      onError={handleLoad}
      style={{
        maxWidth: '100%',
        maxHeight: '80vh',
        opacity: isLoaded ? 1 : 0.2
      }}
     />
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

Live demo

Steps to reproduce:

  1. Open the demo
  2. Wait until the image is loaded (becomes opaque)
  3. Click the "Set picture 2" button
  4. Wait a bit (while the image is loading)

Expected result:

The second image gets opaque after a few seconds (the DelayedImage component gets rerendered with the new state).

Actual result:

The image stays translucent despite the new state that should make it opaque.

I reproduced the bug with React 16.8.2 in Safari 12.0.3 and Chrome 72.0.3626.109 on macOS 10.14.3 (didn't try other versions).

More details about the example

The DelayedImage component is an image that gets translucent while being loaded. The information about whether the image is loading or loaded is stored in a state using the useState hook. The other state hook is used to check whether the image URL has changed (like in the FAQ example).

When the component is rendered for the first time, it works as expected: it is translucent while the image is loading and opaque after the image is loaded. When the src prop is changed (by clicking the "Set picture 2" button), the image gets translucent but doesn't get opaque when it's loaded.

According to the console messages, the correct value (isLoaded === true) is set to the state but React doesn't rerenders the component. React rerenders the component with the correct state when any other DelayedImage props is changed (by clicking the last button or changing a prop using Redux Dev Tools).

@Finesse
Copy link
Author

Finesse commented Feb 21, 2019

This is a possible duplicate of #14812 and #14849

@ioss
Copy link
Contributor

ioss commented Feb 21, 2019

This will also be fixed by #14902

Here is the updated Demo with updated react libs

@gaearon
Copy link
Collaborator

gaearon commented Feb 21, 2019

Fixed in 16.8.3.

@gaearon gaearon closed this as completed Feb 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants