React/Redux/REST: sending an array - javascript

I'm working on a MERN project and I need your support. Which is the best way to get data with a REST call providing an array?
I have an array of ID's and would like to take data using axios and the mongoose method "findById".
I'm sending the array as params from the component; dispatch the action fetchItinerariesId that call the function fetchItinerariesId(favItin_id) where favItin_id is the array of id's, and the reducer add the payload to my state favItineraries: [].
Here below the code:
REST api call:
const express = require("express");
const router = express.Router();
const itineraryModel = require("../../models/itineraryModel");
//! GET FAV ITINERARIES //----------------------------------------------
//* #route GET api/itineraries/:itin_id
//* #desc Get itineraries per fav itin id
//* #access Public
router.get("/itineraries/:favItin_id", (req, res) => {
let itineraryRequestedId = req.params.favItin_id;
itineraryModel
.findById(itineraryRequestedId)
.then(itinerary => {
res.send(itinerary);
})
.catch(err => console.log(err));
});
module.exports = router;
Redux Actions:
import {
FETCH_ITINERARIES_ID_PENDING,
FETCH_ITINERARIES_ID_SUCCESS
} from "./typesActions";
//! FETCH FAVORITES BY USER ID //-------------------------------------------------------------
export function fetchItinerariesIdPending() {
return {
type: FETCH_ITINERARIES_ID_PENDING
};
}
export const fetchItinerariesId = favItin_id => dispatch => {
console.log("inside action fetchItineraries per fav ID");
console.log(favItin_id);
dispatch(fetchItinerariesIdPending());
axios
.get("/api/profile/itineraries", { favItin_id: favItin_id })
.then(res => {
dispatch({
type: FETCH_ITINERARIES_ID_SUCCESS,
payload: res.data
});
});
};
Redux Reducer
FETCH_ITINERARIES_ID_PENDING,
FETCH_ITINERARIES_ID_SUCCESS
} from "../actions/typesActions";
const initialState = {
pending: false,
favItineraries: []
};
function profileReducer(state = initialState, action) {
switch (action.type) {
// GETS FAVORITES FROM ITINERARIES USING ITIN ID
case FETCH_ITINERARIES_ID_PENDING:
return {
...state,
pending: true
};
case FETCH_ITINERARIES_ID_SUCCESS:
return {
...state,
pending: false,
favItineraries: action.payload
};
default:
return state;
}
}
export default profileReducer;
React view component:
/*----- MATERIAL UI -----*/
import Box from "#material-ui/core/Box";...
/*----- REACT/ROUTER/REDUX -----*/
import React, { Component, Fragment } from "react";
import { connect } from "react-redux"; // connect component to redux store.
/*----- COMPONENTS/ACTIONS -----*/
import Navbar from "../components/Navbar";
import ItininerariesList from "../components/ItinerariesList";
import { loadUser } from "../store/actions/authActions";
import { fetchItinerariesId } from "../store/actions/profileActions";
import { fetchActivities } from "../store/actions/activitiesActions";
class Profile extends Component {
componentDidMount() {
this.props.loadUser();
let favItin_id = this.props.favItin_id;
this.props.fetchItinerariesId(favItin_id);
}
render() {
const { favitineraries, activities } = this.props;
return (
<Fragment>
<Container maxWidth="sm">
<Typography component="div">
<Box fontSize="h7.fontSize" textAlign="left" mb="3">
Favorites MYtineraries:
</Box>
<ItininerariesList
itineraries={favitineraries}
activities={activities}
/>
</Typography>
</Container>
<div className="navbar">
<Navbar />
</div>
</Fragment>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
favItin_id: state.auth.user.favorites,
favitineraries: state.profileRed.favItineraries,
activities: state.activitiesRed.activities
};
};
export default connect(mapStateToProps, {
loadUser,
fetchItinerariesId,
fetchActivities
})(Profile);
Any suggestions or guidance would be greatly appreciated. Thank you in advance.

Related

react saga - Filter data based on query's body

In my backend, I use the post request type to fetch the data, so I can send a query or any other information I want in the body, so that the data is fetched on its basis
On the front end, I use React with Saga, and I can fetch data from the server without any problem
The case in which I need help is as follows.
I have a table containing products in the database and I want to divide these products into three pages based on a field in the table
Of course, I was able to do the filtering process in Saga as follows, and there is no problem
How can I pass this query in the image variable according to my need?
Suppose I want to create three pages in React, and for each page I will send a different filter, how do I do that?
I will attach the full code that I tried to do
Waiting for your help
this action
import {
GET_SERVICES_SUPPORT_LIST,
API_RESPONSE_SUCCESS,
API_RESPONSE_ERROR,
} from "./actionType";
// common success
export const ServicesSupportApiResponseSuccess = (actionType, data) => ({
type: API_RESPONSE_SUCCESS,
payload: { actionType, data },
});
// common error
export const ServicesSupportApiResponseError = (actionType, error) => ({
type: API_RESPONSE_ERROR,
payload: { actionType, error },
});
export const getServicesSupportList = services => ({
type: GET_SERVICES_SUPPORT_LIST,
payload: services,
});
this action type
// Actions
export const API_RESPONSE_SUCCESS = "API_RESPONSE_SUCCESS";
export const API_RESPONSE_ERROR = "API_RESPONSE_ERROR";
export const GET_SERVICES_SUPPORT_LIST = "GET_SERVICES_SUPPORT_LIST";
this saga
import { call, put, takeEvery, all, fork } from "redux-saga/effects";
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {
GET_SERVICES_SUPPORT_LIST,
} from "./actionType";
import {
ServicesSupportApiResponseSuccess,
ServicesSupportApiResponseError
} from "./action";
//Include Both Helper File with needed methods
import {
getServicesSupportList as getServicesSupportApi
}
from "../../helpers/backend_helper";
function* getServicesSupport({ payload: services }) {
try {
const response = yield call(getServicesSupportApi, services);
yield put(ServicesSupportApiResponseSuccess(GET_SERVICES_SUPPORT_LIST, response.data));
} catch (error) {
yield put(ServicesSupportApiResponseError(GET_SERVICES_SUPPORT_LIST, error));
}
}
export function* watchGetServicesSupportList() {
yield takeEvery(GET_SERVICES_SUPPORT_LIST, getServicesSupport);
}
function* servicesSupportSaga() {
yield all(
[
fork(watchGetServicesSupportList),
]
);
}
export default servicesSupportSaga;
this hook
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { getServicesSupportList} from "../../../store/actions";
const AllServicesSupportHook = () => {
const dispatch = useDispatch();
//to get state from redux
const { servicesSupportList, loder,error} = useSelector((state) => ({
servicesSupportList: state.ServicesSupport.servicesSupportList,
loder: state.ServicesSupport.loder,
error: state.ServicesSupport.error,
}));
//when first load
useEffect(() => {
if (loder) {
dispatch(getServicesSupportList());
}
}, [dispatch]);
let test ;
try {
if (servicesSupportList.paginator)
test = {query: {categoryTickets : 2} //This is definitely wrong
} catch (e) { }
const getPage = (services) => {
dispatch(getServicesSupportList(services));
}
return [servicesSupportList, loder,error,getPage]
};
export default AllServicesSupportHook;
get list
export const getServicesSupportList = (services) => api.create(url.GET_SERVICES_SUPPORT_LIST,services);
this reducer
import {
GET_SERVICES_SUPPORT_LIST,
API_RESPONSE_SUCCESS,
API_RESPONSE_ERROR,
} from "./actionType";
const INIT_STATE = {
servicesSupportList: [],
loder: true
};
const ServicesSupport = (state = INIT_STATE, action) => {
switch (action.type) {
case API_RESPONSE_SUCCESS:
switch (action.payload.actionType) {
case GET_SERVICES_SUPPORT_LIST:
return {
...state,
servicesSupportList: action.payload.data,
loder: false,
};
default:
return { ...state };
}
case API_RESPONSE_ERROR:
switch (action.payload.actionType) {
case GET_SERVICES_SUPPORT_LIST:
return {
...state,
error: action.payload.error,
loder: true,
};
default:
return { ...state };
}
case GET_SERVICES_SUPPORT_LIST: {
return {
...state,
loder: false,
};
}
default:
return { ...state };
}
};
export default ServicesSupport;
this components
import React from "react";
import { Row, Container ,Alert} from "reactstrap";
import ServicesSupportListItems from "./ServicesSupportListCard";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Loader from "../../Common/Loader";
const ServicesSupportList = ({ data, loder, error }) => {
return (
<Container fluid={true}>
<Row>
{loder === false ? (
data.data ? (
data.data.map((item, index) => {
return (
<ServicesSupportListItems
key={index}
id={item.id}
categoryTickets={item.categoryTickets}
title={item.titleProduct}
description={item.description}
point={item.point}
/>
);
})
) : (<Alert color="danger"> {error} </Alert>)
) : ( <Loader error={error} />
)}
<ToastContainer closeButton={false} />
</Row>
</Container>
);
};
export default ServicesSupportList;
I found the solution, thank you
function* getServicesSupport({ payload: services }) {
try {
var response;
if (services === "Support") {
response = yield call(getServicesSupportApi,{query: {categoryTickets : 2}});
}
if (services === "CardFree") {
response = yield call(getServicesSupportApi,{query: {categoryTickets : 6}});
}
yield put(ServicesSupportApiResponseSuccess(GET_SERVICES_SUPPORT_LIST, response.data));
} catch (error) {
yield put(ServicesSupportApiResponseError(GET_SERVICES_SUPPORT_LIST, error));
}
}

React context api dispatch does not dispatch values when called when called

I am creating an app with reactjs and I am using context api as my state management tool
but the dispatch does not dispatch the values
city shows undefined even after the dispatch has been called.
SearchContext
Here I created the initial state where the city is undefined, date is an empty array and option values are undefined on the initial state
I just have two actions search and reset action which should be dispatched when the user click on a button
import { useReducer } from "react"
import { createContext } from "react"
const INITIAL_STATE = {
city: undefined,
dates: [],
options: {
adult: undefined,
children: undefined,
room: undefined,
}
}
export const SearchContext = createContext(INITIAL_STATE)
const SearchReducer = (state, action) => {
switch (action) {
case "NEW_SEARCH":
return action.payload;
case "RESET_SEARCH":
return INITIAL_STATE;
default:
return state
}
}
export const SearchContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(SearchReducer, INITIAL_STATE)
return (
<SearchContext.Provider
value={{ city: state.city, dates: state.dates, options: state.options, dispatch }}>
{children}
</SearchContext.Provider>
)
}
index.js
I wrapped the whole app with my searchcontext provider so that I can access the values that is passed down to all the components
import App from './App';
import { SearchContextProvider } from './context/SearchContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<SearchContextProvider>
<App />
</SearchContextProvider>
</React.StrictMode>
);
header.js
import { useContext } from "react";
import { SearchContext } from "../../context/SearchContext";
const Header = () => {
const { dispatch } = useContext(SearchContext)
const handleSearch = (e, dispatch) => {
dispatch({ type: "NEW_SEARCH", payload: { destination, "dates": "14-may-2122", options } })
navigate("/hotels", { state: { destination, dates, options } });
};
return (
<div className="header">
<button className="headerBtn" onClick={(e) => handleSearch(e, dispatch)}>
Search
</button>
</div>
)
}
You forgot .type
const SearchReducer = (state, action) => {
switch (action.type) { // <--- add `.type` here
case "NEW_SEARCH":
return action.payload;
case "RESET_SEARCH":
return INITIAL_STATE;
default:
return state
}
}

Server Error Error: Invalid hook call. Hooks can only be called inside of the body of a function component in _app.js

I am a newbie in React and Next JS, I want to set initial auth user data on initial load from the __app.js. But using dispatch throwing error "Invalid hook call". I know according to docs calling hooks in render function is wrong. but I am looking for an alternate solution to this.
How I can set auth data one-time so that will be available for all the pages and components.
I am including my code below.
/contexts/app.js
import { useReducer, useContext, createContext } from 'react'
const AppStateContext = createContext()
const AppDispatchContext = createContext()
const reducer = (state, action) => {
switch (action.type) {
case 'SET_AUTH': {
return state = action.payload
}
default: {
throw new Error(`Unknown action: ${action.type}`)
}
}
}
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {})
return (
<AppDispatchContext.Provider value={dispatch}>
<AppStateContext.Provider value={state}>
{children}
</AppStateContext.Provider>
</AppDispatchContext.Provider>
)
}
export const useAuth = () => useContext(AppStateContext)
export const useDispatchAuth = () => useContext(AppDispatchContext)
/_app.js
import 'bootstrap/dist/css/bootstrap.min.css'
import '../styles/globals.css'
import App from 'next/app'
import Layout from '../components/Layout'
import { mutate } from 'swr'
import { getUser } from '../requests/userApi'
import { AppProvider, useDispatchAuth } from '../contexts/app'
class MyApp extends App {
render() {
const dispatchAuth = useDispatchAuth()
const { Component, pageProps, props } = this.props
// Set initial user data
const setInitialUserData = async () => {
if (props.isServer) {
const initialData = {
loading: false,
loggedIn: (props.user) ? true : false,
user: props.user
}
const auth = await mutate('api-user', initialData, false)
dispatchAuth({
type: 'SET_AUTH',
payload: auth
})
}
}
//----------------------
// Set initial user data
setInitialUserData()
//----------------------
return (
<AppProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppProvider>
)
}
}
MyApp.getInitialProps = async (appContext) => {
let isServer = (appContext.ctx.req) ? true : false
let user = null
let userTypes = {}
// Get user server side
if (isServer) {
await getUser()
.then(response => {
let data = response.data
if (data.status == true) {
// Set user
user = data.data.user
userTypes = data.data.user_types
//---------
}
})
.catch(error => {
//
})
}
//---------------------
return {
props: {
user,
userTypes,
isServer
}
}
}
export default MyApp
I believe this is the intended use of the useEffect hook with an empty array as its second argument:
https://reactjs.org/docs/hooks-effect.html
import {useEffect} from 'react'
class MyApp extends App {
useEffect(()=> {
setInitialUserData()
},[])
render() {
...
}
}

Can't display data using combine reducer REACT

without redux it works so that not a api connection problem
I have an express app connected to react with proxy I have already managed to display my data in react but now i want to make that in redux soo:
There is my problem, i have maked all the reducers/action, store and combine reducer but I didn't see any datas in my page and i haven't any errors
There is my code :
Action
export const api = ext => `http://localhost:8080/${ext}`;
//
// ─── ACTION TYPES ───────────────────────────────────────────────────────────────
//
export const GET_ADVERTS = "GET_ADVERTS";
export const GET_ADVERT = "GET_ADVERT";
//
// ─── ACTION CREATORS ────────────────────────────────────────────────────────────
//
export function getAdverts() {
return dispatch => {
fetch("adverts")
.then(res => res.json())
.then(payload => {
dispatch({ type: GET_ADVERTS, payload });
});
};
}
export function getAdvert(id) {
return dispatch => {
fetch(`adverts/${id}`)
.then(res => res.json())
.then(payload => {
dispatch({ type: GET_ADVERT, payload });
});
};
}
reducer
import { combineReducers } from "redux";
import { GET_ADVERTS, GET_ADVERT } from "../actions/actions";
const INITIAL_STATE = {
adverts: [],
advert: {}
};
function todos(state = INITIAL_STATE, action) {
switch (action.type) {
case GET_ADVERTS:
return { ...state, adverts: action.payload };
case GET_ADVERT:
return { advert: action.payload };
default:
return state;
}
}
const todoApp = combineReducers({
todos
});
export default todoApp;
index.js
//imports
const store = createStore(todoApp, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById("app")
);
My advertlist page :
//imports..
class Adverts extends Component {
componentDidMount() {
this.props.getAdverts();
}
render() {
const { adverts = [] } = this.props;
return (
<div>
<Header />
<h1>Adverts</h1>
{adverts.map(advert => (
<li key={advert._id}>
<a href={"adverts/" + advert._id}>
{advert.name} {advert.surname}
</a>
</li>
))}
<Footer />
</div>
);
}
}
const mapStateToProps = state => ({
adverts: state.adverts
});
export default connect(
mapStateToProps,
{ getAdverts }
)(Adverts);
I think your problem is here:
function mapStateToProps(state) {
return {
**adverts: state.adverts**
};
}
It should work if you change state.adverts to state.todos.adverts:
function mapStateToProps(state) {
return {
adverts: state.todos.adverts
};
}
Because your reducer is called todos, and it has state { adverts }, that's why you cannot access adverts even tho they are obtained.
You can check out working version here: https://codesandbox.io/s/olqxm4mkpq
The problem is, when you just create a store with one reducer without using combine reducer, it is possible to refer it directly in the ContainerS, like this:
const mapStateToProps = state => {
return{
*name of var*: state.adverts /*direct refers to adverts*/
}
}
But, when it use combined-reducer , it has to refer to an exact reducer that you want to use.like this :
const mapStateToProps = state => {
return{
*name of var* : state.todos.adverts (indirect refers to adverts from combined-reducer todos)
}
}

React, TypeError (this.props.data.map is not a function) on an Array obj

Thank you for stopping by to help. I am working with a react/redux app. One of the component is using a lifecyle method to retrieve data from an API. Once recieved, the data JSON data is held within an array. My initialState for the data coming back is an empty array.
When the component listening to the state change is mounted, the data is rendered on to the page, but then 2 seconds later I am getting a
Uncaught TypeError: jobs.map is not a function
Component making the API call using lifecyle method and listening for state change
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getJobs } from '../../actions';
import { Card, Grid, Image, Feed } from 'semantic-ui-react';
// import './home.css';
const renderJobs = jobs => jobs.map((job, i) => (
<Card.Group stackable key={i}>
<Card className="jobscard">
<Card.Content>
<Card.Header href={job.detailUrl} target="_blank">{job.jobTitle}</Card.Header>
<Card.Meta>{job.location}</Card.Meta>
<Card.Description>{job.company}</Card.Description>
</Card.Content>
</Card>
</Card.Group>
));
class GetJobs extends Component {
componentDidMount() {
this.props.getJobs();
}
render() {
const { jobs } = this.props;
return (
<div className="getjobs">
{renderJobs(jobs)}
</div>
);
}
}
export default connect(({ jobs }) => ({ jobs }), { getJobs })(GetJobs);
Action creator/action
export const getJobsRequest = () => fetch('https://shielded-brushlands-43810.herokuapp.com/jobs',
)
.then(res => res.json());
// action creator
export const getJobs = () => ({
type: 'GET_JOBS',
payload: getJobsRequest(),
});
Reducer
import initialState from './initialState';
export default function (jobs = initialState.jobs, action) {
switch (action.type) {
case 'GET_JOBS_PENDING':
return { ...jobs, isFetching: true };
case 'GET_JOBS_FULFILLED':
return action.payload;
case 'GET_JOBS_REJECTED':
return jobs;
default:
return jobs;
}
}
And intial state
export default {
userData: {},
jobs: [],
}
enter image description here
any thoughts on why this is happening?
You can put a simple check to ensure that your jobs is ready before you attempt rendering it.
{jobs.length && renderJobs(jobs)}

Categories