Horai Nuri
Horai Nuri

Reputation: 5568

React, how to return a value from function to component?

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?

Upvotes: 1

Views: 2005

Answers (1)

mehmetseckin
mehmetseckin

Reputation: 3107

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.

Upvotes: 1

Related Questions