JSON Object received from api undefined on Reactjs - javascript

I've been working on my project of making a dashboard. here is my app that received an undefined object from my API. I've been testing on my postman and it's working normally. Here is my code.
This my action.js
import debounce from 'debounce-promise';
import {
START_FETCHING_SOCIAL_MEDIA,
ERROR_FETCHING_SOCIAL_MEDIA,
SUCCESS_FETCHING_SOCIAL_MEDIA,
SET_PAGE,
SET_KEYWORD,
NEXT_PAGE,
PREV_PAGE,
} from './SocialMediaConstants';
import { getSocialMedia } from './api/socialMedia';
let dbouncedFetchSocialMedia = debounce(getSocialMedia, 1000)
export const fetchSocialMedia = () => {
return async (dispatch, getState) => {
dispatch(startFetchingSocialMedia())
let perPage = getState().socialMedia.perPage || 9;
let currentPage = getState().socialMedia.currentPage || 1;
let keyword = getState().socialMedia.keyword || '';
const params = {
limit: perPage,
skip: (currentPage * perPage) - perPage,
q: keyword
}
console.log('fetching...')
try {
console.log('success fetch')
let { data: {data, count}} = await dbouncedFetchSocialMedia(params);
dispatch(successFetchingSocialMedia({data, count}))
console.log(dispatch(successFetchingSocialMedia({data, count})))
} catch (err) {
console.log('failed fetch')
dispatch(errorFetchingSocialMedia(err))
}
}
}
export const startFetchingSocialMedia = () => {
return {
type: START_FETCHING_SOCIAL_MEDIA,
}
}
export const successFetchingSocialMedia = (payload) => {
// console.log(payload)
return {
type: SUCCESS_FETCHING_SOCIAL_MEDIA,
...payload
}
}
export const errorFetchingSocialMedia = () => {
return {
type: ERROR_FETCHING_SOCIAL_MEDIA
}
}
This my reducer.js
import {
START_FETCHING_SOCIAL_MEDIA,
ERROR_FETCHING_SOCIAL_MEDIA,
SUCCESS_FETCHING_SOCIAL_MEDIA,
SET_PAGE,
SET_KEYWORD,
NEXT_PAGE,
PREV_PAGE
} from './SocialMediaConstants'
const statuslist = {
idle: 'idle',
proccess: 'proccess',
success: 'success',
error: 'error'
}
const initialState = {
data: [],
currentPage: 1,
totalItems: -1,
perPage: 6,
keyword: '',
status: statuslist.idle
};
export default function SocialMediaReducer(state = initialState, action){
switch(action.type){
case START_FETCHING_SOCIAL_MEDIA:
return {...state, status: statuslist.proccess, data: []}
case SUCCESS_FETCHING_SOCIAL_MEDIA:
// console.log(...state)
return {...state, data: action.data, totalItems: action.count, status: statuslist.success }
case ERROR_FETCHING_SOCIAL_MEDIA:
return {...state, status: statuslist.error}
case SET_PAGE:
return {...state, currentPage: action.currentPage}
case SET_KEYWORD:
return {...state, keyword: action.keyword}
case NEXT_PAGE:
return {...state, currentPage: state.currentPage + 1}
case PREV_PAGE:
return {...state, currentPage: state.currentPage - 1}
default:
return state;
}
}
This my api/socialMedia.js
import axios from 'axios'
import { config } from '../../../../config'
export async function getSocialMedia(params){
console.log('hit the api')
return await axios.get(`${config.api_host}/api/social-media`, {
params
})
}
This the page
import * as React from "react";
import {useSubheader} from "../../../_metronic/layout";
import { useDispatch, useSelector } from 'react-redux';
import { fetchSocialMedia } from "./SocialMediaActions";
export default function SocialMediaPage(){
let dispatch = useDispatch();
let socMeds = useSelector(state => state.socialMedia);
const suhbeader = useSubheader();
suhbeader.setTitle("Social Media");
React.useEffect(() => {
dispatch(fetchSocialMedia())
}, [dispatch])
return (
<div>
halo
{socMeds && socMeds.data && socMeds.data.map((socialMedia, index) => {
console.log('nungguin ya')
return <div>
{index} - {socialMedia.platform}
</div>
})}
</div>
);
};
This is from the console
enter image description here

Related

How can i test custom fetch hook

I am struggling with an issue with the custom fetch hook.Simply i am trying to test my fetch hook if the data already fetched the hook needs to get data from cache instead of api.
The test case fails and looks like caching mechanism not working, but if i try on the browser with manual prop change caching mechanism works properly.
import { render, waitFor } from "#testing-library/react";
const renderList = (filterParams = testFilterParamsList[0]) =>
render(<List filterParams={filterParams} />);
it("should re-render without fetch", async () => {
const { rerender } = renderList(testFilterParamsList[0]);
rerender(<List filterParams={testFilterParamsList[1]} />);
expect(window.fetch).toHaveBeenCalledTimes(1);
});
// useFetch.js
import {useEffect, useReducer} from "react";
const cache = {};
const FETCH_REQUEST = "FETCH_REQUEST";
const FETCH_SUCCESS = "FETCH_SUCCESS";
const FETCH_ERROR = "FETCH_SUCCESS";
const INITIAL_STATE = {
isPending: false,
error: null,
data: [],
};
const useFetch = ({url, filterOptions}) => {
const [state, dispatch] = useReducer((state, action) => {
switch (action.type) {
case FETCH_REQUEST:return {...INITIAL_STATE, isPending: true};
case FETCH_SUCCESS: return {...INITIAL_STATE, isPending: false, data: action.payload};
case FETCH_ERROR: return {...INITIAL_STATE, isPending: false, error: action.payload};
default: return state;
}
}, INITIAL_STATE);
useEffect(() => {
const fetchData = async () => {
dispatch({type: FETCH_REQUEST});
if (cache[url]) {
const data = cache[url];
dispatch({type: FETCH_SUCCESS, payload: data});
} else {
try {
const response = await window.fetch(url);
let data = await response.json();
cache[url] = data
dispatch({type: FETCH_SUCCESS, payload: data});
} catch (err) {
dispatch({type: FETCH_ERROR, payload: err});
}
}
};
fetchData();
}, [filterOptions, url]);
return state;
};
export default useFetch;
// List.js
import useFetch from "../hooks/useFetch";
export const RocketsList = ({ filterParams }) => {
const { isPending, error, data } = useFetch({
url: "https://api.spacexdata.com/v3/launches/past",
name:filterParams.name,
});
return (
<div>
Doesn't matter
</div>
);
};

Why items appends to the redux rather than replace?

I'm newbie to Reactjs. The problem I'm encountered:
When Article page loads in the first time, all is fine and there are 10 articles shown. When I click on the browser back button, and then I go to the Article page for the second time, the article-list will be duplicated (so, it will be 20 articles). If I do so again, it will be 30 articles and so on ..
I want to know, why the result of API call appends for the Redux and not replace? In other word, how can I clean the Redux on page load every time? The expected result is seeing always 10 item (articles) on the page Article when I open it.
Here is a simplified of the element (for navigating to the list of articles) in the main page:
import Pages from "Constants/Pages";
const Component = () => {
const history = useHistory();
const navigateWithToken = (page) => {
history.push(page);
}
};
return (
<div className="d-flex align-items-center flex-column py-1 ">
<div
className="main-footer-btn-article"
onClick={() => navigateWithToken(Pages.Articles)}
></div>
<span className="main-footer-btn-text">Articles</span>
</div>
)
};
export const ArticlesBtn = memo(Component);
Also, here is the Article page:
import { memo, useEffect } from "react";
import { useHistory } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import PostItems from "SharedComponents/PostItems";
import { getAllPosts } from "Redux/Actions";
import Pages from "Constants/Pages";
const Page = () => {
const posts = useSelector((state) => state?.articles?.posts?.items);
const dispatch = useDispatch();
const { push } = useHistory();
useEffect(() => {
dispatch(getAllPosts());
}, []);
const onClickPost = (item) => {
push({
pathname: Pages.SingleArticle,
state: {
postId: item.id,
title: item.subject,
is_saved: item.is_saved,
},
});
};
return (
<div className="full-height overflow-auto">
{
posts?.map((item, index) => {
return (
<PostItems
{...item}
key={item.id}
index={index}
onClickPost={() => onClickPost(item)}
/>
);
})
}
</div>
);
};
export default memo(Page);
Also here is the API call:
const getAllPosts = (page = 1) => {
return async (dispatch: ReduxDispatch) => {
//"posts?for=for_website"
dispatch(toggleLoading(true));
try {
const { data } = await axios({
method: "GET",
url: "posts?for=for_mobile",
params: { page: page },
});
const items = data?.data?.data;
const pagination = {
current_page: data.data.current_page,
last_page: data.data.last_page,
};
dispatch(
dispatchItemToRedux({
type: ReducerTypes.GET_ALL_POSTS,
payload: {
items,
pagination,
},
})
);
} catch (err) {
return Promise.reject(err);
} finally {
dispatch(toggleLoading(false));
}
};
};
Also, here is the reducer:
import ReducerTypes from "Redux/Types/ReducerTypes";
const INITIAL_STATE = {
posts: {
items: [],
pagination: {}
},
singlePost: {
id: null,
subject: null,
caption: null,
deep_link: null,
short_link: null,
total_comments: null,
total_likes: null,
total_views: null,
created: null,
medias: null,
likes: []
},
postComments: []
};
function articleReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ReducerTypes.GET_ALL_POSTS:
return {
...state,
posts: {
items: state.posts.items.concat(action.payload.items),
pagination: action.payload.pagination
}
};
case ReducerTypes.GET_SINGLE_POST:
return {
...state,
singlePost: action.payload
};
case ReducerTypes.GET_POST_COMMENTS:
return {
...state,
postComments: action.payload
};
case ReducerTypes.GET_POST_LIKES:
return {
...state,
singlePost: {
...state.singlePost,
likes: action.payload
}
};
default:
return state;
};
};
export default articleReducer;
case ReducerTypes.GET_ALL_POSTS:
return {
...state,
posts: {
items: action.payload.items,
pagination: action.payload.pagination
}
};
Try omitting that .concat()

React-Redux: Action is Dispatched but Reducer is not updating the state

I'm trying to check if my store is onboarded or not. for that, I'm making an API call through the redux to check it in the BE and if it's true I'll redirect it to the dashboard. I'm able to get the data successfully from BE, and on success checkIsStoreOnboardedSuccess() is called but in the reducer, the state is not updated with the CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_SUCCESS state in the reducer.
action.js
import * as actionTypes from './index';
import API from '../../api';
export const clearCheckIsStoreOnboarded = () => {
return {
type: actionTypes.CLEAR_CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING,
};
};
export const checkIsStoreOnboarded = (payload) => {
return (dispatch) => {
dispatch(checkIsStoreOnboardedInitiate());
API.getAccountSettings(payload)
.then((response) => {
checkIsStoreOnboardedSuccess(response.data);
})
.catch((err) => {
checkIsStoreOnboardedFailure(err);
});
};
};
const checkIsStoreOnboardedInitiate = () => {
return {
type: actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_START,
};
};
const checkIsStoreOnboardedSuccess = (data) => {
return {
type: actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_SUCCESS,
data: data,
};
};
const checkIsStoreOnboardedFailure = (err) => {
return {
type: actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_FAIL,
data: err,
};
};
reducer.js
import * as actionTypes from '../actions';
const initialState = {
isLoading: true,
isError: false,
isDone: false,
data: [],
error: null,
};
const clearCheckIsStoreOnboarded = () => {
return initialState;
};
const checkIsStoreOnboardedStart = (state) => {
return { ...state, isLoading: true, error: null, isError: false };
};
const checkIsStoreOnboardedSuccess = (state, action) => {
return { ...state, data: action.data, isDone: true, isLoading: false };
};
const checkIsStoreOnboardedFailure = (state, action) => {
return { ...state, error: action.data, isLoading: false, isError: true };
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.CLEAR_CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING:
return clearCheckIsStoreOnboarded();
case actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_START:
return checkIsStoreOnboardedStart(state);
case actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_SUCCESS:
return checkIsStoreOnboardedSuccess(state, action);
case actionTypes.CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_FAIL:
return checkIsStoreOnboardedFailure(state, action);
default:
return state;
}
};
export default reducer;
actionTypes.js
export const CLEAR_CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING = 'CLEAR_CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING';
export const CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_START = 'CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_START';
export const CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_SUCCESS = 'CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_SUCCESS';
export const CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_FAIL = 'CHECK_IS_STORE_ONBOARDED_FOR_ONBOARDING_FAIL';
onboard.js
import React, { useState, useEffect } from 'react';
import { withCookies } from 'react-cookie';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import Crew from './Crew';
import Service from './Services';
import Address from './Address';
import { useStyles } from './css/index.css';
import Header from './header';
import Stepper from './stepper';
import { getStoreID } from '../../../utils';
import {
clearCheckIsStoreOnboarded,
checkIsStoreOnboarded,
} from '../../../store/actions/check-is-store-onboarded-for-onboarding'
import Loader from '../../../components/CircularProgressLoader';
const OnboardScreen = ({
cookies,
clearCheckIsStoreOnboarded,
checkIsStoreOnboarded,
checkIsStoreOnboardedData,
}) => {
const [step, setStep] = useState(0);
// eslint-disable-next-line no-unused-vars
const [width, isDesktop] = useWindowWitdh();
const classes = useStyles(isDesktop);
const store_id = getStoreID(cookies);
useEffect(() => {
checkIsStoreOnboarded({
store_id,
});
}, []);
useEffect(() => () => clearCheckIsStoreOnboarded(), []);
if(checkIsStoreOnboarded.isDone){
<Redirect to='/dashboard'>
}
const updateStep = () => {
const updatedStep = step + 1;
setStep(updatedStep);
};
const onboardingScreenToRender = () => {
switch (step) {
case 0:
return (
<Crew />
);
case 1:
return (
<Service />
);
case 2:
return <Address />;
}
};
return (
<div className={classes.container}>
<Header isDesktop={isDesktop} />
<div className={classes.contentOfContainer}>
<div className={classes.titleHeader}>
Onboarding
</div>
<Stepper stepNumber={step} setStepNumber={setStep} />
{checkIsStoreOnboardedData.isLoading && <Loader />}
</div>
</div>
// <OnboardLoader />
);
};
const mapStateToProps = (state, ownProps) => {
return {
...ownProps,
checkIsStoreOnboardedData: state.checkIsStoreOnboardedForOnboardingReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
checkIsStoreOnboarded: (payload) => dispatch(checkIsStoreOnboarded(payload)),
clearCheckIsStoreOnboarded: () => dispatch(clearCheckIsStoreOnboarded()),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withCookies(OnboardScreen));
You need to dispatch your actions:
export const checkIsStoreOnboarded = (payload) => {
return (dispatch) => {
dispatch(checkIsStoreOnboardedInitiate());
API.getAccountSettings(payload)
.then((response) => {
// here
dispatch(checkIsStoreOnboardedSuccess(response.data));
})
.catch((err) => {
// and here
dispatch(checkIsStoreOnboardedFailure(err)(;
});
};
};
That said: you are writing a very outdated style of Redux here - in modern Redux, all of that would probably be possible with 1/4 of the code. If you are just learning Redux, you are probably following a very outdated tutorial. Modern Redux does not require you to write action type strings or action creators and your reducers can contain mutable logic. Also, it does not use connect unless you are working with legacy class components (which you don't seem to be doing).
I really recommend you to read the official Redux tutorial at https://redux.js.org/tutorials/essentials/part-1-overview-concepts

getCurentPosition() in Reactjs not updating state

I'm trying the get the user current location in my app, but even if I can see it when I console.log it it doesn't work.
I'm using an async function in order to retrieve it but I must be doing something wrong and I cannot figure out what the issue is.
ContextState
import React, { useReducer } from "react";
import RestContext from "./restContext";
import RestReducer from "./restReducer";
import Yelp from "../../Util/Yelp";
import { getCurrentPosition } from "../../Util/GeoLocation";
import {
GET_RESTAURANTS,
GET_INFO_RESTAURANT,
CLEAR_SEARCH,
SET_LOADING,
GET_LOCATION,
} from "../../types";
const RestState = (props) => {
const initalState = {
restaurants: [],
restaurant: {},
loading: false,
location: {},
};
const [state, dispatch] = useReducer(RestReducer, initalState);
// Get Restaurants
const getRestaurants = async (text) => {
setLoading();
let restaurants = await Yelp.searchRestaurants(text);
if (restaurants) {
dispatch({ type: GET_RESTAURANTS, payload: restaurants });
} else {
dispatch({ type: GET_RESTAURANTS, payload: [] });
}
};
// Get info Restaurants
const getRestaurantInfo = async (id) => {
setLoading();
let restaurant = await Yelp.searchRestaurantsInfo(id);
if (restaurant) {
dispatch({ type: GET_INFO_RESTAURANT, payload: restaurant });
} else {
dispatch({ type: GET_INFO_RESTAURANT, payload: {} });
}
};
// Clear search
const clearSearch = () => dispatch({ type: CLEAR_SEARCH });
// Set loading
const setLoading = () => dispatch({ type: SET_LOADING });
// Get location
const fetchCoordinates = async () => {
try {
const coords = await getCurrentPosition();
dispatch({ type: GET_LOCATION, payload: coords });
} catch (error) {
// Handle error
console.error(error);
}
}
return (
<RestContext.Provider
value={{
restaurants: state.restaurants,
restaurant: state.restaurant,
loading: state.loading,
getRestaurants,
clearSearch,
getRestaurantInfo,
fetchCoordinates,
}}
>
{props.children}
</RestContext.Provider>
);
};
export default RestState;
It's reducer
import {
GET_RESTAURANTS,
GET_INFO_RESTAURANT,
CLEAR_SEARCH,
SET_LOADING,
GET_LOCATION,
} from "../../types";
export default (state, action) => {
switch (action.type) {
case GET_RESTAURANTS:
return { ...state, restaurants: action.payload, loading: false };
case GET_INFO_RESTAURANT:
return { ...state, restaurant: action.payload, loading: false };
case CLEAR_SEARCH:
return { ...state, restaurants: [], loading: false };
case SET_LOADING:
return {
...state,
loading: true,
};
case GET_LOCATION:
return { ...state, location: action.payload };
default:
return state;
}
};
And the Home page when it's should be used
import React, { Fragment, useEffect, useContext } from "react";
import Search from "../../Components/restaurants/Search";
import Alert from "../../Components/layout/Alert";
import Navbar from "../../Components/layout/Navbar";
import DisplayRestaurants from "../../Components/layout/DisplayRestaurants";
import Footer from "../../Components/layout/Footer";
import { Waypoint } from "react-waypoint";
import RestContext from "../context/restaurant/restContext";
const Home = () => {
const restContext = useContext(RestContext);
useEffect(() => {
restContext.fetchCoordinates();
// eslint-disable-next-line
}, []);
const handleWaypointEnter = () => {
document.querySelector("nav").classList.remove("fixed");
};
const handleWaypointLeave = () => {
document.querySelector("nav").classList.add("fixed");
};
return (
<section className="main-home">
<Fragment>
<Navbar />
<Search />
<Alert />
<Waypoint onEnter={handleWaypointEnter} onLeave={handleWaypointLeave} />
<DisplayRestaurants />
<Footer />
</Fragment>
</section>
);
};
export default Home;
getCurrentPosition
export function getCurrentPosition(options = {}) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
coord obj
GeolocationCoordinates {latitude: 52.3555177, longitude: -1.1743196999999999, altitude: null, accuracy: 372529, altitudeAccuracy: null, …}
accuracy: 372529
altitude: null
altitudeAccuracy: null
heading: null
latitude: 52.3555177
longitude: -1.1743196999999999
speed: null
__proto__: GeolocationCoordinates
Thanks for your help
can you try this instead?
it returns a promise so in theory should be able to use .then
getCurrentPosition().then((res) => {
console.log(res) // check what `res` is
dispatch({ type: GET_LOCATION, payload: res.cords });
})

How can I get step by step data from api in redux/react by infinite scroll

I want to get 20 posts by scroll down each time how can i do? my project have big Data and I use redux for get data, can I get data step by step? for example get 20 posts for first time and when a user scroll down load the next 20 posts.
I use React Hooks for develop
my posts component source is:
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import PostItem from './PostItem';
import { getPosts } from '../../actions/post';
const Posts = ({ getPosts, post: { posts, loading } }) => {
useEffect(() => {
getPosts();
}, [getPosts]);
return loading ? <Spinner /> : (
{posts.map(post => (
<PostItem key={post._id} post={post} />
))}
)
}
Posts.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts })(Posts)
my action code is:
import { setAlert } from './alert';
import {
GET_POSTS,
POST_ERROR
} from "../actions/types";
// Get Posts
export const getPosts = () => async dispatch => {
try {
const res = await axios.get('/api/ads');
dispatch({
type: GET_POSTS,
payload: res.data
});
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.satusText, status: err.response.satus }
});
}
}```
///////////////////////////////
///////////////AND REDUCER IS :
import {
GET_POSTS,
POST_ERROR
} from '../actions/types';
const initialState = {
posts: [],
loading: true,
error: {}
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_POSTS:
return {
...state,
posts: payload,
loading: false
}
case POST_ERROR:
return {
...state,
error: payload,
loading: false
}
default:
return state;
}
}
You can use react-infinite-scroller library. I've tried to change your Posts method, so maybe it would be useful.but as mentioned in comments you should add pagination to your API.
const Posts = ({ getPosts, post: { posts, loading } }) => {
useEffect(() => {
getPosts();
}, [getPosts]);
const itemsPerPage = 20;
const [hasMoreItems, sethasMoreItems] = useState(true);
const [records, setrecords] = useState(itemsPerPage);
const showItems=(posts)=> {
var items = [];
for (var i = 0; i < records; i++) {
items.push( <PostItem key={posts[i]._id} post={posts[i]} />);
}
return items;
}
const loadMore=()=> {
if (records === posts.length) {
sethasMoreItems(false);
} else {
setTimeout(() => {
setrecords(records + itemsPerPage);
}, 2000);
}
}
return <InfiniteScroll
loadMore={loadMore}
hasMore={hasMoreItems}
loader={<div className="loader"> Loading... </div>}
useWindow={false}
>
{showItems()}
</InfiniteScroll>{" "}
}
Working Codesandbox sample with fake data.

Categories

Resources