Why items appends to the redux rather than replace? - javascript

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()

Related

JSON Object received from api undefined on Reactjs

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

Why is my redux app not caching an async api call in redux-thunk?

Am new to redux.
I am working now on an app that shows a list of soccer leagues in each country.
Firstly, I am fetching a countries list. Afterwards, I am using the country name to loop through all countries to get the soccer leagues. Not every country has a soccer league, so I get some null as response, which I filter out. Then I click on a league and I am redirected to a league page. Now comes the tricky part. When I click "back", I go to my main page, but the whole api call process gets fired again. Why? How to prevent it? how to only use the data, that I fetched ones, and only use it as I need to.
If I would guess, than the mistake is somewhere in the reducer. I try there to cache the fetched api call in an object (data: { ...state.data, ...}), but am not sure, if I do this correctly.
The second place, where I could do a m istake is the useEffect. But of course anything else is also possible.
Please help!
Here is my code:
App.js
I use react-router-dom to move between the conatiners:
import React from 'react';
import {Switch, Route, NavLink, Redirect} from "react-router-dom";
import SignedIn from '../signedIn/signedIn';
import SignedOut from '../signedOut/signedOut';
//Components/Containers
import AllLeagues from '../allLeagues/allLeagues/allLeagues';
import League from "../allLeagues/league/league";
const App = () => {
return (
<div className="App">
<nav>
<NavLink to={"/"}>SEARCH</NavLink>
</nav>
<Switch>
<Route path={"/"} exact component={AllLeagues} />
<Route path={"/allLeagues/:league"} exact component={League} />
<Route path={"/signedin"} exact component={SignedIn} />
<Route path={"/signedout"} exact component={SignedOut} />
<Redirect to={"/"} />
</Switch>
</div>
);
}
export default App;
Here is my Page, where I make the api calls to get the countries and the soccer leagues:
allLeagues.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {Link} from "react-router-dom";
import _ from "lodash";
import shortid from "shortid";
import { allLeagues } from "../../../actions/leagues/allLeagues/allLeagues";
import { allCountries } from "../../../actions/allCountries/allCountries";
//the api provides 255 country names.
const ALL_COUNTRIES_LENGTH = 254;
const AllLeagues = () => {
const dispatch = useDispatch();
const selectAllCountries = useSelector(state => state.allCountries);
const selectAllLeagues = useSelector(state => state.allLeagues);
useEffect(() => {
dispatch(allCountries());
}, [dispatch]);
useEffect(() => {
if(!_.isEmpty(selectAllCountries.data)) {
selectAllCountries.data.countries.map(el => dispatch(allLeagues(el.name_en)));
}
}, [dispatch, selectAllCountries.data]);
let allCountriesArr = [];
let allLeaguesFiltered = [];
let getAllLeagues = [];
allCountriesArr = (Object.values(selectAllLeagues.data));
console.log(Object.values(selectAllLeagues.data));
if(allCountriesArr.length > ALL_COUNTRIES_LENGTH) {
allLeaguesFiltered = allCountriesArr.flat().filter(el => el !== null);
getAllLeagues = allLeaguesFiltered.flat();
}
let getAllZeroDivisionLeagues = [];
let getAllFirstDivisionLeagues = [];
let getAllSecondDivisionLeagues = [];
let getAllThirdDivisionLeagues = [];
if(!_.isEmpty(getAllLeagues)) {
getAllZeroDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "0");
getAllFirstDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "1");
getAllSecondDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "2");
getAllThirdDivisionLeagues = getAllLeagues.filter(el => el.strDivision === "3");
}
const showData = () => {
if(!_.isEmpty(selectAllLeagues.data)) {
return(
<div>
Most Favorited Leagues:
<br/>
{getAllZeroDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
First Leagues:
<br/>
{getAllFirstDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
Second Leagues:
<br/>
{getAllSecondDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
<br/>
<br/>
Third Leagues:
<br/>
{getAllThirdDivisionLeagues.map(el => {
return (
<div key={shortid.generate()}>
<p>{el.strLeague}</p>
<Link to={`/allLeagues/${el.strLeague}`}>View</Link>
</div>
)}
)}
</div>
)
}
if (selectAllLeagues.loading) {
return <p>loading...</p>
}
if (selectAllLeagues.errorMsg !== "") {
return <p>{selectAllLeagues.errorMsg}</p>
}
return <p>Loading...</p>;
}
return (
<div>
<br/>
<br/>
All Leagues:
<br />
<br />
{showData()}
</div>
)
}
export default AllLeagues;
The both action files:
allCountries.js
import { GET_ALL_COUNTRIES_LOADING, GET_ALL_COUNTRIES_SUCCESS, GET_ALL_COUNTRIES_FAIL } from "../index";
import theSportsDB from "../../apis/theSportsDB";
export const allCountries = () => async (dispatch) => {
try {
dispatch ({
type: GET_ALL_COUNTRIES_LOADING
})
const response = await theSportsDB.get("all_countries.php");
dispatch ({
type: GET_ALL_COUNTRIES_SUCCESS,
payload: response.data
})
} catch (e) {
dispatch ({
type: GET_ALL_COUNTRIES_FAIL
})
}
}
and allCountriesReducer:
import {GET_ALL_COUNTRIES_LOADING, GET_ALL_COUNTRIES_SUCCESS, GET_ALL_COUNTRIES_FAIL} from "../../actions/index";
const DefaultState = {
loading: false,
data: [],
errorMsg: ""
};
const AllCountriesReducer = (state = DefaultState, action) => {
switch (action.type){
case GET_ALL_COUNTRIES_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_ALL_COUNTRIES_SUCCESS:
return {
...state,
loading: false,
data: {
...state.data,
countries: action.payload.countries
},
errorMsg: ""
};
case GET_ALL_COUNTRIES_FAIL:
return {
...state,
loading: false,
errorMsg: "unable to get all the Countries"
};
default:
return state;
}
}
export default AllCountriesReducer;
Now the files, with which I fetch the all the leagues (with the country name, that I got from allCountries):
import { GET_ALL_LEAGUES_LOADING, GET_ALL_LEAGUES_SUCCESS, GET_ALL_LEAGUES_FAIL } from "../../index";
import theSportsDB from "../../../apis/theSportsDB";
export const allLeagues = (country) => async (dispatch) => {
try {
dispatch ({
type: GET_ALL_LEAGUES_LOADING
})
const response = await theSportsDB.get(`search_all_leagues.php?c=${country}&s=Soccer`);
dispatch ({
type: GET_ALL_LEAGUES_SUCCESS,
payload: response.data,
countryName: country
})
} catch (e) {
dispatch ({
type: GET_ALL_LEAGUES_FAIL
})
}
}
and the reducer,
allLeaguesReducer.js
import {GET_ALL_LEAGUES_LOADING, GET_ALL_LEAGUES_SUCCESS, GET_ALL_LEAGUES_FAIL} from "../../../actions/index";
const DefaultState = {
loading: false,
data: {},
errorMsg: ""
};
const AllLeaguesReducer = (state = DefaultState, action) => {
switch (action.type){
case GET_ALL_LEAGUES_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_ALL_LEAGUES_SUCCESS:
return {
...state,
loading: false,
data:{
...state.data,
[action.countryName]: action.payload.countrys
},
errorMsg: ""
};
case GET_ALL_LEAGUES_FAIL:
return {
...state,
loading: false,
errorMsg: "unable to get all the leagues"
};
default:
return state;
}
}
export default AllLeaguesReducer;
Also the leagues page itself:
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {Link} from "react-router-dom";
import _ from "lodash";
import shortid from "shortid";
import { getLeague } from "../../../actions/leagues/league/getLeague";
const League = (props) => {
const leagueName = props.match.params.league;
const dispatch = useDispatch();
const selectLeague = useSelector(state => state.league);
useEffect (() => {
dispatch(getLeague(leagueName));
}, [dispatch, leagueName]);
const showLeague = () => {
if(!_.isEmpty(selectLeague.data)) {
return selectLeague.data.teams.map(el => {
return (
<div key={shortid.generate()}>
{el.strTeam}
</div>
)
})
}
if(selectLeague.loading) {
return <p>loading...</p>
}
if(selectLeague.errorMsg !== "") {
return <p>{selectLeague.errorMsg}</p>
}
return <p>Unable to get the league data</p>
}
return (
<div>
<p>{leagueName}</p>
{showLeague()}
<Link to={"/"}>Back</Link>
</div>
)
}
export default League;
its action file:
import { GET_LEAGUE_LOADING, GET_LEAGUE_SUCCESS, GET_LEAGUE_FAIL } from "../../index";
import theSportsDB from "../../../apis/theSportsDB";
export const getLeague = (league) => async (dispatch) => {
try {
dispatch ({
type: GET_LEAGUE_LOADING
})
const response = await theSportsDB.get(`search_all_teams.php?l=${league}`);
dispatch ({
type: GET_LEAGUE_SUCCESS,
payload: response.data,
// leagueName: league
})
} catch (e) {
dispatch ({
type: GET_LEAGUE_FAIL
})
}
}
and the reducer:
import { GET_LEAGUE_LOADING, GET_LEAGUE_SUCCESS, GET_LEAGUE_FAIL } from "../../../actions/index";
const DefaultState = {
loading: false,
data: {},
errorMsg: ""
};
const LeagueReducer = (state = DefaultState, action) => {
switch (action.type) {
case GET_LEAGUE_LOADING:
return {
...state,
loading: true,
errorMsg: ""
};
case GET_LEAGUE_SUCCESS:
return {
...state,
loading: false,
data: action.payload,
errorMsg: ""
};
case GET_LEAGUE_FAIL:
return {
...state,
loading: false,
errorMsg: "league not found"
};
default:
return state
}
}
export default LeagueReducer;
In Redux dev Tools, when I press on back, to get again to my home page, the following is triggered (in status bar):
GET_ALL_COUNTRIES_LOADING
and after some time:
GET_ALL_LEAGUES_SUCCESS
again. So it is making an api call again.
You need to use a conditional in useEffect so that it doesn't run each time you load the page.
Try this:
useEffect(() => {
if (selectAllCountries.data.length < 1) {
disptch(getCountries());
}
})

Unhandled Rejection (TypeError): Cannot read property 'type' of undefined. How to fix that

I don't understand why this error appears. I am trying to create profile feature that will get data with axios and will show it by userid. I tried a lot of things and also i did the same thing with other component and everything worked
ProfileContainer.js
import React from 'react';
import {connect} from 'react-redux';
import Profile from './Profile';
import setUserProfile from '../../redux/profile-reducer'
import * as axios from 'axios';
class ProfileContainer extends React.Component {
componentDidMount() {
axios.get(`https://social-network.samuraijs.com/api/1.0/profile/2`).then(response => {
this.props.setUserProfile(response.data);
});
};
render() {
return (
<Profile {...this.props} profile={this.props.profile} />
)
}
}
let mapStateToProps = (state) => ({
profile: state.profilePage.profile
});
export default connect(mapStateToProps,{setUserProfile})(ProfileContainer);
profile-reducer.js
const ADD_POST = 'ADD-POST'
const UPDATE_NEW_POST_TEXT = 'UPDATE-NEW-POST-TEXT'
const SET_USER_PROFILE = 'SET_USER_PROFILE';
let initialState = {
posts: [
{ id: 1, message: 'How are you bro?)', likesCount: 21312 },
{ id: 2, message: 'Have you ever been to Georgia?', likesCount: 31312312 },
],
newPostText: 'q',
profile: null
};
const profileReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST:
let newPost = {
id: 124331,
message: state.newPostText,
likesCount: 0
}
return { ...state, posts: [...state.posts, newPost], newPostText: '' }
case UPDATE_NEW_POST_TEXT:
return { ...state, newPostText: action.newText };
case SET_USER_PROFILE:
return { ...state, profile: action.profile };
default:
return state;
}
}
export const addPostActionCreator = () => ({ type: ADD_POST });
export const updateNewPostTextActionCreator = (text) => ({ type: UPDATE_NEW_POST_TEXT, newText: text });
export const setUserProfile = (profile) => ({ type: SET_USER_PROFILE, profile });
export default profileReducer;
You must use async/await in your code.
take a look at this article: https://medium.com/#matt.readout/using-javascripts-async-await-syntax-to-fetch-data-in-a-react-app-878b930cdc6f
you will find the same code there.

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