Bypass fetch call in React component

react suspense data fetching
react fetch
react js
react api call tutorial
axios react
react wait for data before render
react-async
reactjs api call example

I am attempting to write a test for a react component that sends out a fetch GET request after mounting. The test just needs to check if the component has rendered and when fetch runs I get this: ReferenceError: fetch is not defined. I have searched around and cant seem to find anything that will fix my problem. I am using jest and Test Utils for testing the components.

My component code:

export class Home extends Component {
    constructor(props) {
        ...
    }
    componentDidMount() {
        fetch('/some/path', {
            headers: {
                'Key1': 'Data1',
                'Key2': Data2
            }
        }).then(response => {
            if (response.status == 200) {
                response.json().then((data) => {
                    this.context.store.dispatch(setAssets(data))
                }
                );
            } else {
                return (
                    <Snackbar
                        open={true}
                        message={"ERROR: " + str(response.status)}
                        autoHideDuration={5000}
                    />
                );
            }
        }).catch(e => {});
           ...
        );
    }
    componentWillUnmount() {
        ...
    }
    logout(e) {
        ...
    }
    render() {
        return (
            <div>
                <AppBar
                    title="Title"
                    iconElementLeft={
                        <IconButton>
                            <NavigationClose />
                        </IconButton>
                    }
                    iconElementRight={
                        <IconMenu
                            iconButtonElement={
                                <IconButton>
                                    <MoreVertIcon />
                                </IconButton>
                            }
                            targetOrigin={{
                                horizontal: 'right',
                                vertical: 'top'
                            }}
                            anchorOrigin={{
                                horizontal: 'right',
                                vertical: 'top'
                            }}
                        >
                            <MenuItem>
                                Help
                            </MenuItem>
                        </IconMenu>
                    }
                />
                {
                    this.context.store.getState().assets.map((asset, i) => {
                        return (
                            <Card
                                title={asset.title}
                                key={i+1}
                            />
                        );
                    })
                }
            </div>
        );
    }
}

Home.contextTypes = {
    store: React.PropTypes.object
}

export default Home;

My Test Code:

var home

describe('Home', () => {
    beforeEach(() => {
        let store = createStore(assets);
        let a = store.dispatch({
                         type: Asset,
                         assets: [{
                                    'id': 1, 
                                    'title': 'TITLE'
                                 }],
                       });
        store.getState().assets = a.assets

        home = TestUtils.renderIntoDocument(
            <MuiThemeProvider muiTheme={getMuiTheme()}>
                <Provider store={store}>
                    <Home />
                </Provider>
            </MuiThemeProvider>
        );
    });
    it('renders the main page, including cards and appbar', () => {}

It errors when trying to render Home into document.

I have tried fetch-mock but this only allows for mock calls for API testing, which I'm not trying to do and doesn't mock the fetch calls in my component.

Mocking Home wont work because its the component I am trying to test. Unless there's a way to mock the componentDidMount() function that I have missed.

I just need a workaround for the fetch call. Any ideas??

EDIT: I'm using React's JSX for the component and JS for the test

Try https://github.com/jhnns/rewire:

rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing

var fetchMock = { ... }

var rewire = require("rewire");
var myComponent = rewire("./myComponent.js");
myComponent.__set__("fetch", fetchMock);

Suspense for Data Fetching (Experimental) – React, In this example, two components wait for an asynchronous API call to fetch some If you prefer to learn when things are more stable, you might prefer to ignore  How to fetch data in React. Newcomers to React often start with applications that don't need data fetching at all. Usually they are confronted with Counter, Todo or TicTacToe applications. That's good, because data fetching adds another layer of complexity to your application while taking the first steps in React.

Unfortunately I'm using babel, which is listed as a limitation for rewire, but I tried it anyways...

I Added:

...
store.getState().assets = a.assets

var fetchMock = function() {return '', 200}
var rewire = require("rewire");
var HomeComponent = rewire('../Home.jsx');
HomeComponent.__set__("fetch", fetchMock);

home = TestUtils.renderIntoDocument(
    <MuiThemeProvider muiTheme={getMuiTheme()}>
        <Provider store={store}>
            <Home />
        ...

And received the error:

Error: Cannot find module '../Home.jsx'
   at Function.Module._resolveFilename (module.js:440:15)
   at internalRewire (node_modules/rewire/lib/rewire.js:23:25)
   at rewire (node_modules/rewire/lib/index.js:11:12)
   at Object.eval (Some/Path/Home-test.js:47:21)

I'm assuming this is because babel:

rename[s] variables in order to emulate certain language features. Rewire will not work in these cases

(Pulled from the link chardy provided me)

However the path isn't a variable, so I wonder if babel is really renaming it, and the path is 100% correct for my component's location. I don't think it's because I'm using JSX because it just cant find the component, its not a compatibility issue... Rewire still may not work even if it finds the file though, unfortunately, but I'd like to give it a shot all the same.

Patterns for data fetching in React, The componentWillUnmount() method is called when our component goes away, and it's a good time to stop the timer by calling clearInterval()  1 Bypass fetch call in React component Jul 11 '16 0 Setting a Text Area's text to align to the bottom Nov 30 '16 0 Multiple setState() calls in a React method: How to make it work “synchronously” Dec 7 '16

I found an answer that worked for me, and it was very simple without including any other dependencies. It was a simple as storing the main function into a variable and overwriting it, then restoring the proper function after the testcase

SOLUTION:

var home

describe('Home', () => {
    const fetch = global.fetch

    beforeEach(() => {
        let store = createStore(assets);
        let a = store.dispatch({
                         type: Asset,
                         assets: [{
                                    'id': 1, 
                                    'title': 'TITLE'
                                 }],
                       });
        store.getState().assets = a.assets

        global.fetch = () => {return Promise.resolve('', 200)}

        home = TestUtils.renderIntoDocument(
            <MuiThemeProvider muiTheme={getMuiTheme()}>
                <Provider store={store}>
                    <Home />
                </Provider>
            </MuiThemeProvider>
        );
    });
    it('renders the main page, including cards and appbar', () => {
        ...
    });
    afterEach(() => {
        global.fetch = fetch;
    });
});

Fetching Data in React using React Async, You're probably used to fetching data in React using axios or fetch. The usual method of handling data fetching is to: Make the API call. Update Skip to main content As such, the component example will look like this; This method will be executed when the component “mounts” (is added to the DOM) for the first time. This method is only executed once during the component’s life. It uses the axios.get function to fetch the data from the subreddit, based on the subreddit prop passed in during render at the bottom.

Using the global context to store components can be brittle and is probably not a good idea for any sizable project.

Instead, you can use the dependency injection (DI) pattern which is a more formalized method for switching out different component dependencies (in this case, fetch) based on your runtime configuration. https://en.wikipedia.org/wiki/Dependency_injection

One tidy way to employ DI is by using an Inversion of Control (IoC) container, such as: https://github.com/jaredhanson/electrolyte

Using data in React with the Fetch API and axios, If you are new to React, and perhaps have only played with building to-do and counter apps, you may not Skip to main content Let's start by creating our component and declaring some default state. We create a method called fetchUser() and use it to do exactly what you might think: request user data  The Fetch API provides an interface for fetching resources. We’ll use it to fetch data from a third-party API and see how to use it when fetching data from an API built in-house. Using Fetch with a third-party API. See the Pen React Fetch API Pen 1 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

What I did is I moved the fetch calls out of the components into a repository class then called that from within the component. That way the components are independent of the data source and I could just switch out the repository for a dummy repository in order to test or change the implementation from doing fetch to getting the data from localStorage, sessionStorage, IndexedDB, the file system, etc.

class Repository {
  constructor() {
    this.url = 'https://api.example.com/api/';
  }

  getUsers() {
    return fetch(this.url + 'users/').then(this._handleResponse);
  }

  _handleResponse(response) {
    const contentType = response.headers.get('Content-Type');
    const isJSON = (contentType && contentType.includes('application/json')) || false;

    if (response.ok && isJSON) {
      return response.text();
    }

    if (response.status === 400 && isJSON) {
      return response.text().then(x => Promise.reject(new ModelStateError(response.status, x)));
    }

    return Promise.reject(new Error(response.status));
  }
}

class ModelStateError extends Error {
  constructor(message, data) {
    super(message);
    this.name = 'ModelStateError';
    this.data = data;
  }

  data() { return this.data; }
}

Usage:

const repository = new Repository();
repository.getUsers().then(
  x => console.log('success', x),
  x => console.error('fail', x)
);

Example:

export class Welcome extends React.Component {
  componentDidMount() {
    const repository = new Repository();
    repository.getUsers().then(
      x => console.log('success', x),
      x => console.error('fail', x)
    );
  }

  render() {
    return <h1>Hello!</h1>;
  }
}

Fetching data from an external API in React, When we don't pass props to a React class component we can omit the After we get the data successfully, fetch calls the first then() method  Some popular ones are Axios, jQuery AJAX, and the browser built-in window.fetch. Where in the component lifecycle should I make an AJAX call? You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.

How to Handle Ajax with React, We will learn about how to use the Ajax call into a React app in this guide. to make HTTP calls into React apps. A few of them are listed below. Axios. Fetch It makes HTTP requests from the node.js platform . using the Axios POST request, bypassing the additional object, which is the request object. react-fetch-component . React component to declaratively fetch data. Install yarn add react-fetch-component or. npm install --save react-fetch-component Usage. You supply a single function as a child of <Fetch /> which receives a single argument as an object. The function will be called anytime the state of the fetch request changes (for example, before a request has been made, while the request is in flight, and after the request returned a response).

heroku/react-refetch: A simple, declarative, and , composable way to fetch data for React components - heroku/react-refetch. If you have a component called Profile that has a userId prop, you can wrap it in  The effect hook called useEffect is used to fetch the data with axios from the API and to set the data in the local state of the component with the state hook's update function. The promise resolving happens with async/await. However, when you run your application, you should stumble into a nasty loop.

AJAX Requests in React: How and Where to Fetch Data, a.k.a. “How do I make API calls in React?” React simply renders components, using data from only two places: props Ignore all of them :) Throughout this AjAX with React tutorial we will cover different ways to do AJAX in React (Axios, jQuery and the Fetch API). We'll see by example how to use the browser Fetch API to send an HTTP request (GET and POST) to Reddit and from where you can make AJAX calls in a React component i.e componentDidMount() vs componentWillMount() vs the ES6 class constructor.

Comments
  • Unfortunately I'm using babel, which is listed as a limitation for rewire, but I tried it anyways. EDIT: Comments are too small for my code, posted details as answer