I'm trying to implement Auth0 into my React project using this tutorial: https://auth0.com/docs/quickstart/spa/react/02-calling-an-api.
Since I don't want to pass the access token on every page, I'm using a global file which I try to use on every Axios API call.
Code (api.js):
import axios from 'axios';
import { useAuth0 } from '#auth0/auth0-react';
const {getAccessTokenSilently} = useAuth0();
const token = await getAccessTokenSilently();
export default axios.create({
baseURL: `http://localhost:8080/`,
headers: {Authorization: `Bearer ${token}`},
timeout: 2000
});
I receive the following error:
Line 4:34: React Hook "useAuth0" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Example call in 'AssignmentSlice.js' (using Redux):
api.post(`assignment/add`, initialAssignment)
.then(res => {
console.log(res.data);
return res.data;
}).catch(err => {
console.log(err.response);
return err.response;
})
What would be the best way/pattern to implement the Auth0 access token?
This mean you need to call useAuth0 from react component to get token. Then passing token to API layer and call API as normal
See example here: https://auth0.github.io/auth0-react/
Related
In my nextjs app, I created a separate file for my axios interceptors and I'm making a request in getServerSideProps() which needs to have a Authorization header with some access token. I'm doing the logic for attaching headers inside mu interceptors but I need to access the cookie outside of getServerSideProps() and in my interceptor. Is there a way to achieve this behavior?
Late answer but may be useful for someone. You'll need to create an API route to retrieve the cookie as usually you'll need to pass the req and res to the cookie lib you're using like cookies-next.
So you create a route like /api/token and then place a code like this:
import { getCookie } from 'cookies-next';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const token = getCookie('site-jwt', { req, res });
res.status(200).json({ token });
}
and then in your interceptor, you do a fetch
const { token } = await fetch('/api/token').then(r => r.json())
According to the latest v6.0.0-alpha.5 release of react router, the history prop has been removed: https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.5
Removed the prop and moved responsibility for setting
up/tearing down the listener (history.listen) into the wrapper
components (, , etc.). is now a
controlled component that just sets up context for the rest of the
app.
Navigating within the react context is simple with the useNavigate hook.
but now in the current V6 how the removal of history prop from BrowserRouter effect the navigation outside of the router context?
For example, if I have a Service class like that:
class Service {
constructor() {
let service = axios.create({
baseURL: BASE_URL,
headers: {
'Content-type': 'application/json; charset=utf-8'
},
timeout: 60000,
withCredentials: true
});
this.service = service;
}
handleError(path, err) {
if (axios.isCancel(err)) { // Catch axios request cancelation
console.log('caught cancel', err.message)
}
else if ( err.response && err.response.status === 401) { // Unauthorized
console.log('user unautorized');
window.location.href = '/auth/login'
}
else {
console.log(`Had Issues getting to the backend, endpoint: ${path}, with data: ${null}`);
console.dir(err);
throw err;
}
}
async get(path, params, loaderTrackerArea) {
try {
const response = await trackPromise(this.service.request({
method: 'GET',
url: path,
params
}), loaderTrackerArea);
return response.data;
} catch (err) {
this.handleError(path, err);
}
}
}
export default new Service();
Now, what I want to get is in the handleError function redirect to login page, currently I used pure javascript solution, what is the best way to achieve that with the current version of react-router-dom?
Thanks all!!!
I had the same problem (since the issue is still in the final release). While there isn't an existing interface to pass in a custom history object, it is actually quite easy to write custom wrapper around the Router component, based on the BrowserRouter implementation.
See https://github.com/remix-run/react-router/discussions/8241#discussioncomment-1677474 for code.
Edit:
As of react-router-dom v6.1.0 HistoryRouter is part of its implementation so you can just directly do:
import {HistoryRouter} from 'react-router-dom'
import {createBrowserHistory} from 'history'
const history = createBrowserHistory()
<HistoryRouter history={history} />
I have the following method in a class:
import axios from 'axios'
public async getData() {
const resp = await axios.get(Endpoints.DATA.URL)
return resp.data
}
Then I am trying to set up a Jest test that does this:
jest.mock('axios')
it('make api call to get data', () => {
component.getData()
expect(axios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
})
The problem is that because I am not mocking the return value, then it gives an error for resp.data because I'm calling data on null or undefined object. I spent at least 2 hours trying various ways to get this working but I can't find a way such that I can mock axios.get with some return value.
Jest's documentation uses JavaScript so they give this example axios.get.mockResolvedValue(resp) but I can't call mockResolvedValue because that method does not exist on axios.get in TypeScript.
Also, if you know other good testing library for React other than Jest that does this stuff easily for TypeScript, feel free to share.
In start of file:
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
Now you can use it as usual mock:
mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
mockedAxios.get.mockResolvedValue({ data: {} });
If you want to use jest.mock with "no-any" try this:
import axios, { AxiosStatic } from 'axios'
interface AxiosMock extends AxiosStatic {
mockResolvedValue: Function
mockRejectedValue: Function
}
jest.mock('axios')
const mockAxios = axios as AxiosMock
it('make api call to get data', () => {
// call this first
mockAxios.mockResolvedValue(yourValue)
component.getData()
expect(mockAxios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
})
I kept running into is not a function issues. If the accepted answer doesn't work for you, then try importing axios with a capital A ie. Axios.
import Axios from 'axios';
jest.mock('Axios');
const mockedAxios = Axios as jest.Mocked<typeof Axios>;
This is what I personally always use.
import axios from 'axios';
jest.mock('axios')
it('...', () => {
(axios.get as jest.Mock).mockImplementationOnce(() => Promise.resolve({}));
// test here
expect(axios.get).toHaveBeenCalled()
}
As of Jest 24.9.0 here is how it works correctly typing both axios and Jest properties.
What we would like for a typed mock is that the mocked object type contains the union of the mocked object type and the type of Jest mocks. As far as I seen non of the current answers enable that.
jest.MockedFunction
jest.MockedClass
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.MockedFunction<typeof axios>;
mockedAxios.mockResolvedValue({ status: 200, data: 'mockResponse' });
// Or:
(mockedAxios.get as jest.MockedFunction<typeof mockedAxios.get>).mockResolvedValue('mockResponse');
As you can see, you can either manually cast what you need or you'll need something to traverse all axios properties/methods to type everything.
To do that (deep mock types) you can use jest.mocked() introduced in Jest 27.4.0
import axios from 'axios';
jest.mock('axios');
const mockedAxios = jest.mocked(axios, true);
mockedAxios.mockImplementation() // correctly typed
mockedAxios.get.mockImplementation() // also correctly typed
but I can't call mockResolvedValue because that method does not exist on axios.get in TypeScript
You can use an assertion:
(axios.get as any).mockResolvedValue(resp)
I found a neat solution using the sinon library npm install sinon #types/sinon --save-dev.
Then the testing code becomes:
let component: Component
let axiosStub: SinonStub
beforeAll(() => {
component = new Component({})
axiosStub = sinon.stub(axios, 'get')
})
afterAll(() => {
axiosStub.restore()
})
it('make api call to get data', async () => {
// set up behavior
axiosStub.withArgs(Endpoints.DATA.URL).returns({data: []})
// method under test
const res = await component.getData()
// assertions
expect(res).toEqual([])
})
Another option is to use jest.spyOn:
import axios from "axios";
jest.spyOn(axios, "get").mockImplementation(() => Promise.resolve({data: []}));
This also gives you the benefit of having a mocked method that you can test against, for example:
import axios from "axios";
// ...
const mockedGet = jest
.spyOn(axios, "get")
.mockImplementation(() => Promise.resolve({data: []}));
// ...
expect(mockedGet).toBeCalledWith('https://example.api?q=abc&k=123');
I have a React container in which I am making the API call and would like to be able to test this using jest and enzyme but unsure how to.
This is my code:
import React from "react";
import Search from "../../components/Search";
import { API_KEY } from "../../../config";
class SearchContainer extends React.Component {
state = {
articles: []
};
performSearch = event => {
fetch(
`http://content.guardianapis.com/search?q=${event}&api-key=${API_KEY}`
)
.then(response => response.json())
.then(data => this.setState({ articles: data.response.results }));
};
render() {
return (
<Search
performSearch={this.performSearch}
articles={this.state.articles}
/>
);
}
}
export default SearchContainer;
That's a great thing about unit testing, they force you to write better code. So to properly test this component, you should do the following:
Extract performSearch from The component into a separate file e.g. api.js
Mock performSearch in your api.js file (jest: mock a module)
Now you can test that the fetch function was called.
Note that with this code organization you could separately test your API calls and your SearchContainer without calling your API service.
I would approach this by extracting performSearch out into a module that wraps fetch. See this great article on testing things you don't own.
After that, you may not need SearchContainer any more if you store the articles state within the Search component. Since you're already using dependency injection with the performSearch property, you can pass in a mock object in place of it and use jest.fn() to ensure it is called.
For example:
const fakePerformSearch = jest.fn();
const component = Shallow(<Search performSearch={fakePerformSearch}/>);
expect(fakePerformSearch).toHaveBeenCalled();
And then test your new fetch wrapper as you would any JavaScript.
A lot of the other answers recommend using Jest's import mocker or a mock function, however, this tests implementation over behavior.
It's better to stub the environment instead of the tools. Let's write a test using an HTTP interceptor like nock. The beauty of this is you can migrate to different data fetching tools or make changes the fetch behavior and get feedback from your tests.
// src/SearchContainer/SearchContainer.test.js
import React from "react";
import nock from "nock";
import {mount} from "enzyme";
import Search from "../../components/Search";
import { API_KEY } from "../../../config";
describe('<SearchContainer />', async () => {
it('searches for articles', () => {
const scope = nock('http://content.guardianapis.com')
.get('/search')
.query({'api-keys': API_KEY, {q: 'some article'}})
.reply(200, {
results: [...]
})
const wrapper = mount(<SearchContainer />);
const searchInput = wrapper.find('[data-test-id="search-input"]');
await searchInput.simulate('change', { target: { value: 'some article' } });
const articles = wrapper.find('[data-test-id="articles"]');
expect(articles.length > 0).toBe(true);
expect(scope.isDone()).toBe(true);
});
});
For a deeper dive on testing API calls, I wrote a blog post Testing Components that make API calls.
Hi all i wanna make API/Server call for retrieve the data into my react web application how to do that . and which is the best tool for make API call should i use AJAX or react fetch
There is no specific way in react, you free to choose.
I would recommend Fetch api, it works beautifully. So if you are familiar with ES6 promises etc then go for it.
Your other options are XMLHttpRequest, axios
React only deals with the view layer, so ideally it is not supposed to make api calls.
Hook react up with other patterns like flux or redux etc and see the magic.
There are couple of other ways to make api calls:
XMLHttpRequest
Fetch
Superagent
I prefer Fetch as it is designed to be more extensible and efficient
Also, you can see the comparison table below for more options:
https://npmcompare.com/compare/axios,isomorphic-fetch,mappersmith,superagent-bluebird-promise,then-request.
Updated on 13 Feb 17:
#Ramusesan : Below is my working code.
dataAction.js
import * as types from '../constants/ActionTypes';
export function loadData(data) {
return {type: types.LOAD_DATA, data};
}
export function getDataById(id) {
return {type: types.GET_DATA_BY_ID, id};
}
export function loadCompleteData() {
return function(dispatch, getState) {
return fetch("localhost:9090/data/", {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then ((response) => {
dispatch(loadProfiles(response));
})
.catch(error => console.log(error));
}
}
configure-store.js
import {createStore, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers/index';
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
return store;
}