1

I want to do a basic if, else operation inside my component, for that I need to return a [boolean] after checking if the image loaded correctly or not.

export const Suggest = (props) => {
  function handleLoad(url) {
    let image = new Image();
    image.src = url;

    image.onload = function() {    
      return false;
    }

    image.onerror = function() {    
      return true;
    }
  }

  return (
    <div>
      { 
        props.state.suggestions.map((item, i) => 
          <div key={i}>
            <span>Boolean {i} = {handleLoad(item.image) ? 'True' : 'False'}</span>
          </div>
        )
      }
    </div>
  )
}

Unfortunately calling the function handleLoad(item.image) returns null.

How should I fix this problem?

1 Answer 1

1

The handleLoad function does not return anything, and will return undefined by default. The onload and onerror are assigned new functions.

Since you're loading an image (an asynchronous operation), there is no way to return the result synchronously. You will need to use promises, e.g.

function handleLoad(url) {
  return new Promise(function (resolve, reject) {
    let image = new Image();
    image.src = url;

    image.onload = function() { 
        resolve();
    }

    image.onerror = function() {    
        reject(new Error('Image loading failed'));
    } 
  });
}

Then, you can load your images on componentDidMount, and use the then callback to set your state accordingly:

// This is just a helper method to set the loaded
// flag and update the state for a given suggestion.
// Added it to avoid repeating ourselves.
setSuggestionLoaded = (suggestionIndex, isLoaded) => {
    let suggestions = this.state.suggestions;
    suggestions[suggestionIndex].loaded = isLoaded;
    this.setState({ suggestions: suggestions });
}

componentDidMount() {
    this.state.suggestions.map((suggestion, i) => {
        handleLoad(suggestion)
        .then(() => {
            setSuggestionLoaded(i, true);
        })
        .catch((err) => {
            setSuggestionLoaded(i, false);
        }));
    })
}

Then you can render your state, it will be updated when the loading operations finish or fail.

  return (
    <div>
      { 
        props.state.suggestions.map((item, i) => 
          <div key={i}>
            <span>Boolean {i} = {item.loaded ? 'True' : 'False'}</span>
          </div>
        )
      }
    </div>
  )

Another option to do this would be via plain old callbacks, e.g.:

function handleLoad(url, successCallback, errorCallback) {
    let image = new Image();
    image.src = url;

    image.onload = function() { 
        if(successCallback) successCallback();
    }

    image.onerror = function() {
        if(errorCallback) errorCallback(new Error('Image loading failed'));
    } 
}


componentDidMount() {
    this.state.suggestions.map((suggestion, i) => {
        handleLoad(suggestion,
            () => {                
                setSuggestionLoaded(i, true);
            },
            (err) => {
                setSuggestionLoaded(i, false);
            }
        );
    })
}

But I'd personally recommend the promises approach. I'd go even further and recommend you to read about the new async / await syntax which essentially is a pretty way to write promise-based asynchronous JavaScript.

Sign up to request clarification or add additional context in comments.

2 Comments

Out of scope question, but shouldn't you use the setState() function instead, when setting the loaded value to each suggestion?
Ah yeah, very good shout! I forgot to add that, after setting the loaded property, you need to use the setState function. Editing now.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.