11

I'm trying to start an API call from a component when a user clicks a button. One URL param depends on the word a user selected before clicking the button. The fetch function is in an external function. When I call the function on button click and console log the result, it shows undefined, probably because of the async nature of the function.

How can I solve this if I want to put the fetch response into the App component state?

import { fetchAPI } from '../fetchAPI';

export default class App extends Component {

constructor() {
    super();
    this.toggleButtonState = this.toggleButtonState.bind(this);
    state = { ... }
}

toggleButtonState() {
    let selectedWord = window.getSelection().toString();
    fetchAPI(selectedWord);
    // call the fetchAPI function and put the result into state
}

export function fetchAPI(param) {
    // param is a highlighted word from the user before it clicked the button
    fetch('https://api.com/?param=' + param)
    .then(function(result) {
        return result;
    });
 }
2
  • .then(function(result) { return result; }); - this is redundant! It's like having function noop(v) { return v;} ... would you ever use that? ilke let x = noop(4) rather than let x = 4? Commented Oct 30, 2018 at 22:29
  • I disagree with you Bravo. Its done many a times to organize code in such a way that Component files do not send any back end calls but will be done from the invoked function which is imported from some other module/file Commented Oct 30, 2018 at 22:31

3 Answers 3

11

You have to return the fetch request from your fetchAPI function, and you also want to add an additional then and give it a function in which you put the result in the state in the toggleButtonState function.

In your example the then inside the fetchAPI function is redundant, since it just returns the value as is. You can remove that and still get the same result.

Example

function fetch() {
  return new Promise(resolve => setTimeout(() => resolve(42), 1000));
}

function fetchAPI(param) {
  // param is a highlighted word from the user before it clicked the button
  return fetch("https://api.com/?param=" + param);
}

class App extends React.Component {
  state = { result: null };

  toggleButtonState = () => {
    let selectedWord = window.getSelection().toString();
    fetchAPI(selectedWord).then(result => {
      this.setState({ result });
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.toggleButtonState}> Click me </button>
        <div>{this.state.result}</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

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

2 Comments

Thanks for the answer. Why a setTimeout and why resolve(42)?
@Doge You're welcome! The URL in your question didn't work, so I just used that as a silly example.
3
toggleButtonState() {
  let selectedWord = window.getSelection().toString();
  fetchAPI(selectedWord).then(result => this.setState({result});
}

export function fetchAPI(param) {

  // param is a highlighted word from the user before it clicked the button
  return fetch('https://api.com/?param=' + param)
}

This will solve your problem. If you don't want to change the fetch API like that just add 'return' before calling fetch as shown below.

toggleButtonState() {
  let selectedWord = window.getSelection().toString();
  fetchAPI(selectedWord).then(result => this.setState({result});
}

export function fetchAPI(param) {
  return fetch('https://api.com/?param=' + param)
    .then(function(result){
      return result;
    });
}

3 Comments

What's going on with your answer?
fetchAPI function returns the promise and thus the function attached to promise resolution's is invoked which is inside the React component method and the state can be set.
Got you. So please note that I added return keyword to fetch call inside of fetchAPI in exported function. That might sound logically same in this case but probably in reality the user would be doing many more things inside of the resolved method attached to then in fetch call inside of fetchAPI and thus I suggested to just prefix return in fetchAPI method instead of removing the .then statement completely as said in first part
1

One solution is to provide a callback to your fetchAPI() function:

function fetchAPI(param, callback) {
  // param is a highlighted word from the user before it clicked the button
  return fetch("https://api.com/?param=" + param)
    .then(callback);
}

You can call the modified function like this:

fetchAPI(selectedWord, result =>
  this.setState({ result });
);

Note that this follows the same general principle as the other answers. You need to call setState() with the correct this reference. You can only do so from a function inside your component class.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.