I'm developing a Movie App. I dont have problem about receiving data and viewing it on the screen. But when i want to add a Loader to my project. It never goes away instead of staying for 1-2 seconds.
const Movies = () => {
const { movies, isLoading } = useGlobalContext();
if (isLoading) {
return <div className="loading"></div>;
}
return (
<section className="movies">
{movies.map((movie) => {
const {
imdbID: key,
Poster: poster,
Title: title,
Year,
year,
} = movie;
return (
<Link to={`/movies/${key}`} key={key} className="movie">
<article>
<img src={poster} alt={title} />
<div className="movie-info">
<h4 className="title">{title}</h4>
<p>{year}</p>
</div>
</article>
</Link>
);
})}
</section>
);
};
It's my context page useGlobalContext and isLoading coming from here
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const [isLoading, setIsLoading] = useState(true);
const [isError, setError] = useState({ show: false, msg: "" });
const [movies, setMovies] = useState([]);
const [query, setQuery] = useState("spider-man");
const fetchMovies = async (url) => {
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
if (data.Response === "True") {
setMovies(data.Search);
setError({ show: false, msg: "" });
} else {
setError({ show: true, msg: data.Error });
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
fetchMovies(`${API_ENDPOINT}&s=${query}`);
}, []);
return (
<AppContext.Provider
value={{ isLoading, isError, movies, query, setQuery }}
>
{children}
</AppContext.Provider>
);
};
export const useGlobalContext = () => {
return useContext(AppContext);
};
export { AppContext, AppProvider };
You never set your isLoading state back to false after you loaded your assets
const AppProvider = ({ children }) => {
const [isLoading, setIsLoading] = useState(true);
const [isError, setError] = useState({ show: false, msg: "" });
const [movies, setMovies] = useState([]);
const [query, setQuery] = useState("spider-man");
const fetchMovies = async (url) => {
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
if (data.Response === "True") {
setMovies(data.Search);
setError({ show: false, msg: "" });
} else {
setError({ show: true, msg: data.Error });
}
setIsLoading(false); // <--- added this bit
} catch (error) {
console.log(error);
setIsLoading(false); // <--- added this bit
}
};
useEffect(() => {
fetchMovies(`${API_ENDPOINT}&s=${query}`);
}, []);
return (
<AppContext.Provider
value={{ isLoading, isError, movies, query, setQuery }}
>
{children}
</AppContext.Provider>
);
};
Related
When a user logs in, the background of their liked photos should change. Why does the background color only change when the page is reloaded and not after a successful login? There are too many requests happening at the same time and I don't know how to handle them properly. Maybe someone has any ideas.?
Login.js
import { useAppContext } from "../context/appContext";
function Login() {
const { handleClick } = useAppContext();
return (
<div>
<button onClick={() => handleClick()}>Log in</button>
</div>
);
}
export default Login;
Home.js
import { useAppContext } from "../context/appContext";
function Home() {
const [page, setPage] = useState(1);
const [query, setQuery] = useState("landscape");
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(false);
const { token, username, getUserProfile, getToken } =
useAppContext();
const clientId = process.env.REACT_APP_UNSPLASH_KEY;
useEffect(() => {
if (window.location.search.includes("code=") && !token) {
getToken();
}
if (token) {
getUserProfile();
}
}, [token]);
const fetchImages = () => {
setLoading(true);
let params = {
page: page,
query: query,
per_page: 30,
};
let headers = {};
if (username) {
headers = {
Authorization: `Bearer ${token}`,
};
} else {
params = { ...params, client_id: clientId };
}
axios
.get("https://api.unsplash.com/search/photos", { headers, params })
.then((response) => {
setImages([...images, ...response.data.results]);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setLoading(false);
});
setPage(page + 1);
};
useEffect(() => {
fetchImages();
setQuery("");
}, []);
return (
<div>
<Login />
{loading && <Loader />}
<ImageList images={images} fetchImages={fetchImages} />
</div>
);
}
export default Home;
appContext.js
import React, { useReducer, useContext } from "react";
import reducer from "./reducer";
import axios from "axios";
import {
SET_TOKEN,
SET_USERNAME,
LOGOUT_USER,
} from "./actions";
const token = localStorage.getItem("token");
const username = localStorage.getItem("username");
const initialState = {
token: token,
username: username,
};
const client_id = process.env.REACT_APP_UNSPLASH_KEY;
const redirect_uri = "http://localhost:3000/";
const api_auth_uri = "https://unsplash.com/oauth/authorize";
const api_token_uri = "https://unsplash.com/oauth/token";
const response_type = "code";
const scope = [
"public",
"read_user",
"write_user",
"read_photos",
"write_photos",
"write_likes",
"write_followers",
"read_collections",
"write_collections",
];
const client_secret = process.env.REACT_APP_UNSPLASH_SECRET;
const grant_type = "authorization_code";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const handleClick = async () => {
window.location.href = `${api_auth_uri}?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope.join(
"+"
)}`;
};
const getToken = async () => {
const coder = window.location.search.split("code=")[1];
try {
const { data } = await axios.post(
`${api_token_uri}?client_id=${client_id}&client_secret=${client_secret}&redirect_uri=${redirect_uri}&code=${coder}&grant_type=${grant_type}`
);
const { access_token } = data;
localStorage.setItem("token", access_token);
dispatch({
type: SET_TOKEN,
payload: { access_token },
});
const newUrl = window.location.href.split("?")[0];
history.replaceState({}, document.title, newUrl);
} catch (error) {
logoutUser();
}
};
const getUserProfile = async () => {
try {
const { data } = await axios.get(`https://api.unsplash.com/me`, {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + state.token,
},
});
const { username } = data;
localStorage.setItem("username", username);
dispatch({
type: SET_USERNAME,
payload: { username },
});
} catch (error) {
console.log(error)
}
};
const logoutUser = () => {
dispatch({ type: LOGOUT_USER });
localStorage.removeItem("token");
};
return (
<AppContext.Provider
value={{
...state,
handleClick,
getToken,
getUserProfile,
logoutUser,
}}
>
{children}
</AppContext.Provider>
);
};
const useAppContext = () => useContext(AppContext);
export { AppProvider, initialState, useAppContext };
ImageList.js
import React, { useState } from "react";
import "../styles/ImageList.scss";
function ImageList({ images }) {
return (
<div className="result">
{images?.map((image, index) => (
<div
style={{
backgroundColor: image.liked_by_user ? "red" : "",
}}
key={image.id}
>
<div key={image.id}>
<img src={image.urls.small} alt={image.alt_description} />
</div>
</div>
))}
</div>
);
}
export default ImageList;
and also useEffect also not working in my file. i tried many way but useEffect not working. if i comments everything file and try then useEffect working else not working.
and also i am getting null value from
const { currentVideo } = useSelector((state) => state.video);
this is my useEffect ->
useEffect(() => {
const fetchData = async () => {
try {
const videoRes = await axiosInstance.get(`/videos/find/${path}`, {
withCredentials: true,
});
// console.log(videoRes);
const updatedView = await axiosInstance.put(`videos/view/${path}`);
// console.log(updatedView.data, "view is updating");
const channelRes = await axiosInstance.get(
`/users/find/${videoRes.data.userId}`,
{ withCredentials: true }
);
setChannel(channelRes.data);
dispatch(fetchSuccess(videoRes.data));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
fetchData();
}, [path, dispatch]);
here i am getting null value
const Video = () => {
const { currentUser } = useSelector((state) => state.user);
const { currentVideo } = useSelector((state) => state.video);
const dispatch = useDispatch();
const path = useLocation().pathname.split("/")[2];
// console.log(currentVideo); // getting null
const [channel, setChannel] = useState({});
and my full video.jsx is
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import ThumbUpOutlinedIcon from "#mui/icons-material/ThumbUpOutlined";
import ThumbDownOffAltOutlinedIcon from "#mui/icons-material/ThumbDownOffAltOutlined";
import ReplyOutlinedIcon from "#mui/icons-material/ReplyOutlined";
import AddTaskOutlinedIcon from "#mui/icons-material/AddTaskOutlined";
import ThumbUpIcon from "#mui/icons-material/ThumbUp";
import ThumbDownIcon from "#mui/icons-material/ThumbDown";
import Comments from "../components/Comments";
import { useDispatch, useSelector } from "react-redux";
import { useLocation } from "react-router-dom";
import axios from "axios";
import { fetchSuccess, like, dislike } from "../redux/videoSlice";
import axiosInstance from "../axios";
import { subscription } from "../redux/userSlice";
import Recommendation from "../components/Recommendation";
import { format } from "timeago.js";
const Video = () => {
const { currentUser } = useSelector((state) => state.user);
const { currentVideo } = useSelector((state) => state.video);
const dispatch = useDispatch();
const path = useLocation().pathname.split("/")[2];
// console.log(currentVideo); // getting null
const [channel, setChannel] = useState({});
/*
// console.log(path); ok hai video user id aa rahi hai.
// its working its getting all the data.
const test = async () => {
const isWorking = await axios.get(
"http://localhost:5000/api/videos/find/63931e44de7c22e61c4ffd6c"
);
console.log(isWorking.data);
console.log(isWorking.data.videoUrl);
};
const videoRes = test();
console.log(videoRes);
*/
// {withCredentials: true}
useEffect(() => {
const fetchData = async () => {
try {
const videoRes = await axiosInstance.get(`/videos/find/${path}`, {
withCredentials: true,
});
// console.log(videoRes);
const updatedView = await axiosInstance.put(`videos/view/${path}`);
// console.log(updatedView.data, "view is updating");
const channelRes = await axiosInstance.get(
`/users/find/${videoRes.data.userId}`,
{ withCredentials: true }
);
setChannel(channelRes.data);
dispatch(fetchSuccess(videoRes.data));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
fetchData();
}, [path, dispatch]);
const handleLike = async () => {
try {
await axiosInstance.put(`/users/like/${currentVideo._id}`, {
withCredentials: true,
});
dispatch(like(currentUser._id));
} catch (err) {
console.log(err);
return "opps something went wrong!";
}
};
const handleDislike = async () => {
try {
await axiosInstance.put(`/users/dislike/${currentVideo._id}`, {
withCredentials: true,
});
dispatch(dislike(currentUser._id));
} catch (err) {
console.log(err);
}
};
const handleSub = async () => {
currentUser.subscribedUsers.includes(channel._id)
? await axiosInstance.put(`/users/unsub/${channel._id}`, {
withCredentials: true,
})
: await axiosInstance.put(`/users/sub/${channel._id}`, {
withCredentials: true,
});
dispatch(subscription(channel._id));
};
if (!currentUser) return "Loading....";
return (
<Container>
<Content>
<VideoWrapper>
<VideoFrame src={currentVideo?.videoUrl} controls />
</VideoWrapper>
<Title>{currentVideo?.title}</Title>
<Details>
<Info>
{currentVideo?.views} views •{format(currentVideo?.createdAt)}
</Info>
<Buttons>
<Button onClick={() => handleLike()}>
{currentVideo?.likes.includes(currentUser._id) ? (
<ThumbUpIcon />
) : (
<ThumbUpOutlinedIcon />
)}{" "}
{currentVideo?.likes.length}
</Button>
<Button onClick={() => handleDislike()}>
{currentVideo?.dislikes.includes(currentUser._id) ? (
<ThumbDownIcon />
) : (
<ThumbDownOffAltOutlinedIcon />
)}
Dislike
</Button>
<Button>
<ReplyOutlinedIcon /> Share
</Button>
<Button>
<AddTaskOutlinedIcon /> Save
</Button>
</Buttons>
</Details>
<Hr />
<Channel>
<ChannelInfo>
<Image src={channel.img} />
<ChannelDetail>
<ChannelName>{channel.name}</ChannelName>
<ChannelCounter>{channel.subscribers} subscribers</ChannelCounter>
<Description>{currentVideo?.desc}</Description>
</ChannelDetail>
</ChannelInfo>
<Subscribe onClick={handleSub}>
{currentUser.subscribedUsers.includes(channel._id)
? "SUBSCRIBED"
: "SUBSCRIBE"}
</Subscribe>
</Channel>
<Hr />
<Comments videoId={currentVideo?._id} />
</Content>
<Recommendation tags={currentVideo?.tags} />
</Container>
);
};
export default Video;
and this is my redux videoslice file videoSlice.js
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
currentVideo: null,
loading: false,
error: false,
};
export const videoSlice = createSlice({
name: "video",
initialState,
reducers: {
fetchStart: (state) => {
state.loading = true;
},
fetchSuccess: (state, action) => {
state.loading = false;
state.currentVideo = action.payload;
},
fetchFailure: (state) => {
state.loading = false;
state.error = true;
},
like: (state, action) => {
if (!state.currentVideo.likes.includes(action.payload)) {
state.currentVideo.likes.push(action.payload);
state.currentVideo.dislikes.splice(
state.currentVideo.dislikes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
dislike: (state, action) => {
if (!state.currentVideo.dislikes.includes(action.payload)) {
state.currentVideo.dislikes.push(action.payload);
state.currentVideo.likes.splice(
state.currentVideo.likes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
views: (state, action) => {
if (state.currentVideo.views.includes(action.payload)) {
state.currentVideo.views.push(action.payload);
}
},
},
});
export const { fetchStart, fetchSuccess, fetchFailure, like, dislike, views } =
videoSlice.actions;
export default videoSlice.reducer;
in browser i am also getting null
i am trying to call my api and render data. but useeffect not working in my code. also from useSelector i am getting null value.
I am creating an app about goal tracker. When I logout from an account, everything goes okay except the the logout gets stuck in pending state.
There is also error in console saying Cannot read properties of null (reading 'token') Dashboard.jsx:20.
Dashboard.jsx
import React from 'react';
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import GoalForm from '../components/GoalForm';
import Spinner from '../components/Spinner';
import { getGoals, reset } from '../features/goals/goalSlice';
import GoalItem from '../components/GoalItem';
function Dashboard() {
const navigate = useNavigate();
const dispatch = useDispatch();
const { user } = useSelector(store => store.auth);
const { goals, isLoading, isError, message } = useSelector(
store => store.goals,
);
useEffect(() => {
if (isError) console.log(message);
if (!user) navigate('/login');
dispatch(getGoals());
return () => {
dispatch(reset());
};
}, [user, isError, message, navigate, dispatch]);
if (isLoading) return <Spinner />;
return (
<>
<section className='heading'>
<h1>Welcome {user && user.name}</h1>
<p>Goals Dashboard</p>
</section>
<GoalForm />
<section className='content'>
{goals.length > 0 ? (
<div className='goals'>
{goals.map(goal => (
<GoalItem key={goal._id} goal={goal} />
))}
</div>
) : (
<h3>You have not set any goals</h3>
)}
</section>
</>
);
}
export default Dashboard;
goalSlice.js
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import goalService from './goalService';
const initialState = {
goals: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
};
// Create goal
export const createGoal = createAsyncThunk(
'goals/create',
async (goalData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await goalService.createGoal(goalData, token);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
},
);
// Get Goals
export const getGoals = createAsyncThunk('goals/get', async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await goalService.getGoals(token);
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
});
// Delete goal
export const deleteGoal = createAsyncThunk(
'goals/delete',
async (id, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await goalService.deleteGoal(id, token);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
},
);
export const goalSlice = createSlice({
name: 'goal',
initialState,
reducers: {
reset: state => initialState,
},
extraReducers: builder => {
builder
.addCase(createGoal.pending, state => {
state.isLoading = true;
})
.addCase(createGoal.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.goals.push(action.payload);
})
.addCase(createGoal.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getGoals.pending, state => {
state.isLoading = true;
})
.addCase(getGoals.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.goals = action.payload;
})
.addCase(getGoals.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(deleteGoal.pending, state => {
state.isLoading = true;
})
.addCase(deleteGoal.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.goals = state.goals.filter(
goal => goal._id !== action.payload.id,
);
})
.addCase(deleteGoal.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
});
},
});
export const { reset } = goalSlice.actions;
export default goalSlice.reducer;
goalService.js
import axios from 'axios';
const API_URL = '/api/goals';
// Create goal
const createGoal = async (goalData, token) => {
const config = {
headers: {
'x-auth-token': `${token}`,
},
};
const response = await axios.post(API_URL, goalData, config);
return response.data;
};
// Get goals
const getGoals = async token => {
const config = {
headers: {
'x-auth-token': `${token}`,
},
};
const response = await axios.get(API_URL, config);
return response.data;
};
// Delete goal
const deleteGoal = async (goalId, token) => {
const config = {
headers: {
'x-auth-token': `${token}`,
},
};
const response = await axios.get(`${API_URL}/${goalId}`, config);
// console.log(response);
return response.data;
};
const goalService = {
createGoal,
getGoals,
deleteGoal,
};
export default goalService;
And this is logout part: builder.addCase(logout.fulfilled, state => {state.user = null});
When i try to logout, the user is logged out but the error appears continuously, like re-rendering the page. The page is re-rendered itself and state is stuck in logout.
Delete Goal is also not working
You must check if the user is logged in before attempt to get the goals
useEffect(() => {
if (isError) console.log(message);
if (!user) {
navigate('/login');
} else {
dispatch(getGoals());
}
return () => {
dispatch(reset());
};
}, [user, isError, message, navigate, dispatch]);
I have implemented the following code to fetch data and render a component if everything goes well along with checking loading, error states.
import { useEffect, useState } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
const fetchPosts = () => {
setLoader(true);
setTimeout(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
// throw new Error("Some error occured");
const data = await response.json();
if (data.error) {
setError({ status: true, message: data.error });
} else {
setPosts(data);
}
setLoader(false);
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
setLoader(false);
}
}, 2000);
};
useEffect(() => {
fetchPosts();
}, []);
if (loader) return <h3>Loading...</h3>;
if (error.status) return <h3>Error: {error.message}</h3>;
return (
<div>
<h1>Posts</h1>
{posts.length === 0 && <h3>There are no posts</h3>}
{posts.length > 0 && (
<div>
{posts.map((post) => (
<Post post={post} key={post.id} />
))}
</div>
)}
</div>
);
}
export default Posts;
Is this the right way to handle loading, error and success states when fetching data? or is there a better and more elegant solution than repeating this for every component?
Instead of checking for data.error in the try block, you could check for response.ok; if it is true, call response.json(), otherwise throw an error.
Also move the setLoader call to the finally block to avoid the duplicate calls in try and catch blocks.
try {
const response = await fetch(...);
if (response.ok) {
let data = await response.json();
setPosts(data);
} else {
throw new Error(/* error message */);
}
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
} finally {
setLoader(false);
}
If you want to check for data.error property in a response, you can change the following if condition
if (response.ok) {
to
if (response.ok && !data.error) {
is there a better and more elegant solution than repeating this for
every component?
Make a custom hook to make the fetch request and use that in every component that needs to fetch data from the backend.
const useFetch = (apiUrl, initialValue) => {
const [data, setData] = useState(initialValue);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
useEffect(() => {
async function fetchData = (url) => {
setLoader(true);
try {
const response = await fetch(url);
if (response.ok) {
let responseData = await response.json();
setData(responseData);
} else {
throw new Error(/* error message */);
}
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
} finally {
setLoader(false);
}
}
fetchData(apiUrl);
}, [apiUrl]);
return [data, error, loader];
};
Your solution should be good enough to do, but, to me, I would prefer not to set timeout for getting data, and I will use .then and .catch for better readable and look cleaner to me
import { useEffect, useState } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
const fetchPosts = () => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => response.json())
.then(data => {
setPosts(data);
setLoader(false);
})
.catch(error =>{
console.log("error", error);
setError({ status: true, message: error.message });
setLoader(false);
});
};
useEffect(() => {
setLoader(true);
fetchPosts();
}, []);
if (loader) return <h3>Loading...</h3>;
if (error.status) return <h3>Error: {error.message}</h3>;
return (
<div>
<h1>Posts</h1>
{posts.length === 0 && <h3>There are no posts</h3>}
{posts.length > 0 && (
<div>
{posts.map((post) => (
<Post post={post} key={post.id} />
))}
</div>
)}
</div>
);
}
export default Posts;
I have created this custom hook to fetch data:
const useSuggestionsApi = () => {
const [data, setData] = useState({ suggestions: [] });
const [url, setUrl] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = () => {
setError(false);
setLoading(true);
if(url) {
fetch(url).then((res) => {
if (res.status !== 200) {
console.error(`It seems there was an problem fetching the result. Status Code: ${res.status}`)
return;
}
res.json().then((fetchedData) => {
setData(fetchedData)
})
}).catch(() => {
setError(true)
})
setLoading(false);
};
}
fetchData();
}, [url]);
return [{ data, loading, error }, setUrl];
}
export default useSuggestionsApi;
It used used in this component to render the response (suggestions).
const SearchSuggestions = ({ query, setQuery}) => {
const [{ data }, doFetch] = useSuggestionsApi();
const { suggestions } = data;
useEffect(() => {
const encodedURI = encodeURI(`http://localhost:3000/search?q=${query}`);
doFetch(encodedURI);
}, [doFetch, query]);
return (
<div className="search-suggestions__container">
<ul className="search-suggestions__list">
{suggestions.map((suggestion) => {
return (
<li className="search-suggestions__list-item" key={uuid()}>
<span>
{suggestion.searchterm}
</span>
</li>
)
})}
</ul>
</div>
);
};
export default SearchSuggestions;
Now I would like to write some unit test for the SearchSuggestions component but I am lost on how to mock the returned data from useSuggestionApi. I tried importing useSuggestionApi as a module and then mocking the response like this but with no success:
describe('SearchSuggestions', () => {
const wrapper = shallow(<SearchSuggestions/>)
it('test if correct amount of list-item elements are rendered', () => {
jest.mock("../hooks/useSuggestionsApi", () => ({
useSuggestionsApi: () => mockResponse
}));
expect(wrapper.find('.search-suggestions__list').children()).toHaveLength(mockResponse.data.suggestions.length);
});
})
I am new to testing React components so very grateful for any input!
This works:
jest.mock('../hooks/useSuggestionsApi', () => {
return jest.fn(() => [{data: mockResponse}, jest.fn()]
)
})
describe('SearchSuggestions', () => {
const wrapper = shallow(<SearchSuggestions query="jas"/>)
it('correct amount of list-items gets rendered according to fetched data', () => {
expect(wrapper.find('.search-suggestions__list').children()).toHaveLength(mockResponse.suggestions.length);
});
})