Catch error after promise initialize warning in unit test - reactjs

When I start tests a warning appears:
Warning: An update to inside a test was not wrapped in
act(...).
it caused by catching error:
const getPeople = () => {
const url = "https://api";
axios(url)
.then((res) => {
setPeople(res.data.results);
})
.catch(function () {
setError(true);
});
};
Everything works fine if I delete catch, but I want to have it, is there a way to do this?

You can try this out
const getPeople = () => {
const url = "https://api";
await axios.get(url)
.then((res) => {
setPeople(res.data.results);
})
.catch(err => {
setError(true);
});
};

Related

How to write a test to handle an Axios error

I have a GET Axios request in which I pass a github user and get information about him. I need to process the option when the request gives an error, which I process in catch (). How can i do this?
//get user
async componentDidMount() {
getUser(this.props.user)
.then((response) => {
this.setState(response.data);
})
.catch((error) => {
this.setState({
error: "request error"
});
});
}
//jest
jest.mock("axios");
describe("UserList Component", () => {
it("should return user login", async () => {
const user = { data: "login1" };
axios.get.mockResolvedValueOnce(user);
const res = await getUser(user);
expect(res.data).toContain("login1");
});
it("should return error", async () => {
//Here I need to write a test in which I check the error so that the error text matches
});
this is the answer testing the catch block using jest
expect(() => {
const model = new Sample(resolvedSample)
}).toThrow(TypeError);

Possible Unhandled Promise Rejection (id: 0): React Native

I know what caused this error, I explicitly removed the header from axios call to check if the code can handle the error. But my question is I have a catch block in place, but I'm still getting this -> Possible Unhandled Promise Rejection
const getUser1 = () => {
userService.getUser1().then((res) => {
setId(res.data._id);
return Promise.resolve();
}).catch((error) => Promise.reject(error));
};
const getUserComments = () => {
commentsService.getUserComments(‘user1’).then((res) => {
setComments(res.data)
return Promise.resolve();
}).catch((err) => Promise.reject(err));
};
useEffect(() => {
const onInit = async () => {
await Promise.all([
getUser1(),
getUserComments(),
]).catch((ex) => console.log(ex));
};
onInit();
}, []);
Try this version:
const getUser1 = async () => {
try{
const {data} = await userService.getUser1()
setId(data._id);
}
catch(err){
throw new Error(err)
}
};
const getUserComments = async () => {
try{
const {data} = await commentsService.getUserComments(‘user1’)
setComments(data)
}
catch(err){
throw new Error(err)
}
};
const init = useCallback(async () =>{
try{
return await Promise.all([
getUser1(),
getUserComments(),
])
}
catch(err){
console.error(err)
}
}, [])
useEffect(() => {
init();
}, [init]);

TypeError: Cannot read properties of undefined (reading 'setRestaurants')

I'm working on a project where I am trying to fetch a list of restaurants from a database and display them on the screen.
When I run the code below I get this error:
TypeError: Cannot read properties of undefined (reading
'setRestaurants')
at CustomerDashPage.js:39
at async fetchList (CustomerDashPage.js:39)
at async getList (CustomerDashPage.js:32)
I know the fetch from the database works as I can console.log restaurants after I get them and all the tags from the database are the same as what is initially in the useState.
const [restaurants, setRestaurants] = useState([
{
Restaurant_id: "R763567026",
Restaurant_menuID: 0,
Restaurant_name: "Boston Pizza",
Restaurant_address: "271 Blackmarsh Rd",
Restaurant_postal: "P1Z 7A5",
Restaurant_username: "firstrest",
Restaurant_orders: ["O415052628", "O321764897", "O252073901", "O724516036"],
Restaurant_menuID: "M859068353",
Restaurant_category: "Japanese",
Restaurant_availability: true,
Restaurant_openHour: "11h00",
Restaurant_closeHour: "22h00",
},
]);
useEffect(() => {
const getList = async () => {
const fetchRest = await fetchList('R763567026');
}
getList();
}, [])
const fetchList = async (id) => {
try {
const resp = await fetch("/restaurant/id/" + id)
.then((resp) => resp.json())
.then(data => this.setRestaurants(data)).then(console.log(restaurants))
.catch(err => console.log(err));
}
catch (err) {
throw err;
console.log(err);
}
return true;
}
//Controls what happens when a restaurant is selected.
const selectRestaurant = async (id) => {
console.log(id);
};
return (
<div>
<Header />
<RestaurantList
itemList={restaurants}
component={RestaurantCard}
onSelect={selectRestaurant}
>
{" "}
</RestaurantList>
</div>
);
};
export default CustomerDash;
Any help would be much appreciated
As Abu mentioned in his answer, you need to call setRestaurants, not this.setRestaurants. Also, since you are using async/await syntax, you don't need all of those .then() calls.
const fetchList = async (id) => {
const response = await fetch(`/restaurant/id/${id}`).catch((err) => throw err);
const json = await response.json();
setRestaurants(json);
console.log(restaurants);
return true;
};
It's functional component so use setRestaurants instead of this.setRestaurants
const fetchList = async (id) => {
try {
const resp = await fetch("/restaurant/id/" + id)
.then((resp) => resp.json())
.then(data =>
setRestaurants(data))
.catch(err => console.log(err));
}
catch (err) {
throw err;
console.log(err);
}
}
After updating state, you won't get state value instantly. so your console.log(restaurants) won't work.

Call API only after setting loading state

Since, setState in a functional component do not return a promise, how do we set a loading state and then call an API. I have seen people doing it like the one below. I think the axios call will not wait for the loading state to be successfully set before executing. Is there any other better way to solve this without writing the fetch part in an useEffect with the dependency of the loading state?
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
try {
const response = await axios('http://localhost/products');
setData(response.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchProduct();
}, [productId]);
you can try something like this
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
await axios.get('http://localhost/products')
.then(response => {
setLoading(false);
setData(response.data);
}).catch(error => {
setLoading(false);
setError(error);
})
};
fetchProduct();
}, [productId]);

How to mock a fetch call that is within an arrow function?

I'm trying to test the invocation of a function that deletes specific data saved in a database in React. The problem is I want to only mock the fetch call and have everything else run as usual because right now whenever tests are run the data gets deleted in the database.
Here is my code for the delete function:
deleteEvent = async () => {
try {
await fetch(
"api url",
{
method: "DELETE",
}
)
.then((res) => res.json())
.then(
(result) => {
console.log(result);
},
(error) => {
console.log(error);
}
);
} catch (error) {
console.error(error);
}
this.props.history.push("/eventList");
};
And here is my test code:
test("deleteEvent function works", (done) => {
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
jest.spyOn(global, "fetch").mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});
I get number times called: 0 for the expect(global.fetch).toHaveBeenCalledTimes(1);
and a Received: undefined for the expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
Any help would be great
Instead of using spyOn(global, fetch), try this:
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});

Resources