Calling API in redux - reactjs

//Store
import { configureStore } from "#reduxjs/toolkit";
import { currencyListSlice } from "./Reducers/CurrencyListReducer";
export const store = configureStore({
reducer: {
currencyList: currencyListSlice.reducer,
}
}
)
export default store
//CurrencyListReducer
import { createSlice } from "#reduxjs/toolkit"
export const loadCurrencyList = () => {
return async (dispatch, getState) => {
const data = await fetch(API-Key)
const payload = await data.json()
dispatch({
type: 'currencyList/setCurrencyList',
payload: payload
})
}
}
const options = {
name: 'currencyList',
initialState: [],
reducers: {
setCurrencyList(state, action) {
return action.payload
}
}
}
export const currencyListSlice = createSlice(options)
//CurrencyList Component
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { currencyListSlice } from '../../Reducers/CurrencyListReducer'
const selectCurrencyList = state => state.CurrencyList
export const CurrencyList = () => {
const dispatch = useDispatch()
const currencyList = useSelector(selectCurrencyList)
const { loadCurrencyList } = currencyListSlice.actions
useEffect(() => {
dispatch(loadCurrencyList())
}, [dispatch, loadCurrencyList])
console.log(currencyList)
return (
<div>
/*Some elements here*/
</div>
)
}
I'm working with redux for the first time and having some real problem in calling API and storing data in store. The problem is I'm not getting anything from API but the console.log(currencyList) just gives me undefined. I tried calling API directly in reducer but that too didn't work out. I'm a newbie to redux and calling the API in redux is being a difficult task for me. Forgive any silly mistake(if present).

try reading this: createAsyncThunk

Related

Cant use Redux thunk show list products

Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
Cant use Redux thunk show list products
import axios from "axios";
import {
ALL_PRODUCT_FAIL,
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
CLEAR_ERRORS,
} from "../constants/productsConstants";
// Get All Products
export const getProduct = () =>
async (dispatch) => {
try {
dispatch({ type: ALL_PRODUCT_REQUEST });
let link = `http://localhost:8080/api/v1/products/`;
const { data } = await axios.get(link);
dispatch({
type: ALL_PRODUCT_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_PRODUCT_FAIL,
// payload: error.response.data,
});
}
};
// Clearing Errors
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
export const ALL_PRODUCT_REQUEST = "ALL_PRODUCT_REQUEST";
export const ALL_PRODUCT_SUCCESS = "ALL_PRODUCT_SUCCESS";
export const ALL_PRODUCT_FAIL = "ALL_PRODUCT_FAIL";
export const CLEAR_ERRORS = "CLEAR_ERRORS";
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
CLEAR_ERRORS,
} from "../constants/productsConstants";
export const productsReducer = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCT_SUCCESS:
return {
loading: true,
products: [],
};
case ALL_PRODUCT_REQUEST:
return {
loading: false,
products: action.payload.products,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
// Clearing Errors
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import {
productsReducer,
} from "./reducers/productsReducer";
const reducer = combineReducers({
products: productsReducer,
});
// let initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
// initialState,
{},
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
import { React, useEffect } from "react";
import Layout from "../../components/Layouts/Layout"
import Link from "next/link";
import { useDispatch, useSelector } from "react-redux"
import { getProduct, clearErrors } from "../../redux/actions/productsAction"
const ProductList = () => {
const dispatch = useDispatch();
const { products, error } = useSelector((state) => state.products);
useEffect(() => {
if (error) {
dispatch(clearErrors());
}
dispatch(getProduct())
console.log("products: ", products)
}, [dispatch, error]);
return (
)
}
export default ProductList

Err Call Api on redux observable using Axios

I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
ERR: fetchData is not a function
I need help
Contsants
export const FETCH_DATA = "FETCH_DATA";
export const FETCH_DATA_FAIL = "FETCH_DATA_FAIL ";
Action
import { FETCH_DATA, FETCH_DATA_FAIL } from "../contsants/contsants";
export const fetchData = (exampleData = {}) => ({
type: FETCH_DATA,
payload: exampleData
});
export const fetchDataFail = () => ({
type: FETCH_DATA_FAIL
});
Store
import { createStore } from "redux";
import rootReducer from "../Reducer/reducer";
const store = createStore(rootReducer);
export default store;
Reducer
import { FETCH_DATA, FETCH_DATA_FAIL } from "../contsants/contsants";
import { combineReducers } from "redux";
const initialState = {};
export const exampleData = (state = initialState, action: any) => {
switch (action.type) {
case FETCH_DATA:
return action.payload;
case FETCH_DATA_FAIL:
return {};
default:
return state;
}
};
export default combineReducers({
exampleData
});
Epics
import "rxjs";
import axios from "axios";
import { from, of } from "rxjs";
import { mergeMap, map, catchError } from "rxjs/operators";
import { ofType } from "redux-observable";
import { FETCH_DATA } from "../contsants/contsants";
import { fetchData, fetchDataFail } from "../Actions/action"
export const exampleEpic = (action$: any) =>
action$.pipe(
ofType(FETCH_DATA),
mergeMap((action) =>
from(axios.get("jsonplaceholder.typicode.com/todos/1")).pipe(
map((response) => fetchData(response.data)),
catchError(() => of(fetchDataFail()))
)
)
);
App
import { fetchData } from './Actions/action'
import { connect } from "react-redux";
function App(data: any, fetchData: any) {
const handleClickShowsTodos = () => {
fetchData()
console.log(data);
}
return (
<div>
<input type="text" />
<button onClick={handleClickShowsTodos}>ShowsTodo</button>
</div>
);
}
const mapStateToProps = (state: any) => {
return {
data: state
};
};
function mapDispatchToProps(dispatch: any) {
return {
fetchData: () => {
console.log('dispatch')
dispatch(fetchData())
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);

Redux-Saga: TypeError: Cannot read properties of undefined (reading 'data')

I was trying to run my Redux app with redux-saga.
Basically on my store.js I have the following codes:
import { applyMiddleware, createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import logger from "redux-logger";
import rootReducer from "./reducers/rootReducer";
import rootSaga from "./sagas/userSagas";
const sagaMiddleware = createSagaMiddleware();
const middleware = [sagaMiddleware];
if (process.env_NODE_ENV === "development") {
middleware.push(logger);
}
const store = createStore(rootReducer, applyMiddleware(...middleware));
sagaMiddleware.run(rootSaga);
export default store;
My usersApi.js looks something like this:
import axios from "axios";
export const loadUsersApi = async () => {
await axios.get("http://localhost:5000/users");
};
And here is my userSagas:
import * as type from "../actionType";
import {
take,
takeEvery,
takeLatest,
put,
all,
delay,
fork,
call,
} from "redux-saga/effects";
import { loadUsersSuccess, loadUsersError } from "../actions/userAction";
import { loadUsersApi } from "../services/userApi";
export function* onLoadUsersStartAsync() {
try {
const response = yield call(loadUsersApi);
if (response.status === 200) {
yield delay(500);
yield put(loadUsersSuccess(response.data));
}
} catch (error) {
yield put(loadUsersError(error.response.data));
}
}
export function* onLoadUsers() {
yield takeEvery(type.LOAD_USERS_START, onLoadUsersStartAsync);
}
const userSagas = [fork(onLoadUsers)];
export default function* rootSaga() {
yield all([...userSagas]);
}
When I run this on my HomePage.js file where I load and dispatch the data:
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { loadUsersStart } from "../redux/actions/userAction";
export default function HomePage() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUsersStart());
}, [dispatch]);
return (
<div>
<h1>Home</h1>
</div>
);
}
It gave me this error:
[HMR] Waiting for update signal from WDS...
index.js:1 TypeError: Cannot read properties of undefined (reading 'data')
at onLoadUsersStartAsync (userSagas.js:25)
at onLoadUsersStartAsync.next (<anonymous>)
at next (redux-saga-core.esm.js:1157)
at currCb (redux-saga-core.esm.js:1251)
index.js:1 The above error occurred in task onLoadUsersStartAsync
created by takeEvery(LOAD_USERS_START, onLoadUsersStartAsync)
created by onLoadUsers
created by rootSaga
Tasks cancelled due to error:
takeEvery(LOAD_USERS_START, onLoadUsersStartAsync)
I am not sure what's causing this error, but even the logger of my app doesn't even show the actions being dispatched.
Any idea how can I fix this issue?
You need to return promise from
export const loadUsersApi = () => {
return axios.get("http://localhost:5000/users");
};
Maybe next time try to use typescript. It will prevent You from similar mistakes
Alternatively, you can build your redux without using redux-saga.
This is how I usually set them up:-
A. Reducer
/slices/auth.js
import { createSlice } from '#reduxjs/toolkit'
const initialState = {
users: [],
loading: false,
success: false,
error: false,
message: ''
}
export const usersSlice = createSlice({
name: 'users',
initialState,
// // The `reducers` field lets us define reducers and generate associated actions
reducers: {
setUsers: (state, action) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.users = action.payload
},
setLoading: (state, action) => {
state.loading = action.payload
},
setSuccess: (state, action) => {
state.success = action.payload.status
state.message = action.payload.message
},
setError: (state, action) => {
state.error = action.payload.status
state.message = action.payload.message
}
}
})
export const { setUsers, setLoading, setSuccess, setError, setMessage } = usersSlice.actions;
// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectUsers = (state) => state.users.users
export const selectLoading = (state) => state.users.loading
export const selectSuccess = (state) => state.users.success
export const selectError = (state) => state.users.error
export const selectMessage = (state) => state.users.message
export default usersSlice.reducer;
B. Store
store.js
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit'
import usersReducer from '../slices/users'
export const store = configureStore({
reducer: {
users: usersReducer
},
middleware: getDefaultMiddleware({
serializableCheck: false
})
})
C. App Component
import { Provider } from 'react-redux'
import { store } from '../app/store'
export default function App() {
return {
<>
<Provider store={store}>
{/* your content... */}
</Provider>
</>
}
}
D. Component (where you use the redux)
import { useContext, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { selectUsers, selectLoading, selectSuccess, selectError, selectMessage, setUsers, setLoading, setSuccess, setError } from '../slices/users'
import axios from 'axios'
export default function HomePage() {
const dispatch = useDispatch()
const users = useSelector(selectUser)
const loading = useSelector(selectLoading)
const success = useSelector(selectSuccess)
const error = useSelector(selectorError)
const message = useSelector(selectorMessage)
useEffect(() => {
async function init() {
dispatch(setLoading(true))
const response = await axios.get('http://localhost:5000/users')
if(response?.status == 200) {
dispatch(setUsers(response?.data?.data))
dispatch(setSuccess({ status: true, message: 'Successfully get users data.' }))
} else {
dispatch(setError({ status: true, message: 'Failed to get data.' }))
}
dispatch(setLoading(false))
}
return init()
}, [])
return {
<>
{user}
</>
}
}

Dispatch is not a function error with createSlice()

My objective is to get the customer details from a fake API through redux. I have migrated to using createSlice() to reduce the boilerplate, but I keep having that error. I have tried and researched as much as I can, but to no avail.
Here is a self contained code: https://stackblitz.com/edit/react-gbhdd9?file=src/App.js
App.js
import React, { useCallback, useState, useEffect } from 'react';
import { customerApi } from './__fakeAPI__/customerApi';
import { getCustomer as getCustomerSlice } from './slices/customers';
export default function App() {
const [customer, setCustomer] = useState(null);
const getCustomer = useCallback(async () => {
try {
//const data = await customerApi.getCustomer(); //works but not what I wanted
const data = getCustomerSlice();
setCustomer(data);
} catch (err) {
console.error(err);
}
}, []);
useEffect(() => {
getCustomer();
}, [getCustomer]);
return <div>{console.log(customer)}</div>;
}
slices/customers.js
import { createSlice } from '#reduxjs/toolkit';
import { customerApi } from '../__fakeAPI__/customerApi';
const initialState = {
customer: {}
};
const slice = createSlice({
name: 'customers',
initialState,
reducers: {
getCustomer(state, action) {
state.customer = action.payload;
}
}
});
export const { reducer } = slice;
export const getCustomer = () => async dispatch => {
const data = await customerApi.getCustomer();
//dispatch seems to not be working
dispatch(slice.actions.getCustomer(data));
};
export default slice;
You needed to dispatch your action to make it work, to access customer you can use useSelector to get access to your state :
https://stackblitz.com/edit/react-v4u5ft?file=src/App.js
You need to make a few changes to App.js to make this work.
The getCustomerSlice() method is an action creator and in your case, it returns a thunk, and to get access to the dispatch method in a thunk, you must first dispatch the action/function returned from that action creator i.e in App.js
export default function App() {
...
const getCustomer = useCallback(async () => {
try {
const data = dispatch(getCustomerSlice());
...
}, []);
...
}
To get access to the dispatch method in App.js, import the useDispatch from react-redux, useDispatch is a function that returns the dispatch method i.e
...
import { useDispatch } from "react-redux"
...
export default function App() {
const dispatch = useDispatch()
...
const getCustomer = useCallback(async () => {
try {
const data = dispatch(getCustomerSlice());
...
}, []);
...
}
When you do this redux-thunk will automatically supply dispatch, getState as parameters to any function that is returned from an action creator. This will cause your implementation to update your store properly.
Since you are expecting a return value from your dispatch method, make this modification to your customers.js file
...
export const getCustomer = () => async dispatch => {
const data = await customerApi.getCustomer();
//dispatch seems to not be working
dispatch(slice.actions.getCustomer(data));
// return the data
return data;
};
...
then in your App.js file await the dispatch function since you defined it as async
...
import { useDispatch } from "react-redux"
...
export default function App() {
const dispatch = useDispatch()
...
const getCustomer = useCallback(async () => {
try {
const data = await dispatch(getCustomerSlice());
...
}, []);
...
}
TLDR
Your App.js should now look like this.
import React, { useCallback, useState, useEffect } from 'react';
import { customerApi } from './__fakeAPI__/customerApi';
import { useSelector, useDispatch} from "react-redux"
import { getCustomer as getCustomerSlice } from './slices/customers';
export default function App() {
const dispatch = useDispatch()
const [customer, setCustomer] = useState(null);
const getCustomer = useCallback(async () => {
try {
//const data = await customerApi.getCustomer(); //works but not what I wanted
const data = await dispatch(getCustomerSlice());
setCustomer(data);
} catch (err) {
console.error(err);
}
}, []);
useEffect(() => {
getCustomer();
}, [getCustomer]);
return <div>{console.log("customers",customer)}</div>;
}
And your customers.js like this
import { createSlice } from '#reduxjs/toolkit';
import { customerApi } from '../__fakeAPI__/customerApi';
const initialState = {
customer: {}
};
const slice = createSlice({
name: 'customers',
initialState,
reducers: {
getCustomer(state, action) {
state.customer = action.payload;
}
}
});
export const { reducer } = slice;
export const getCustomer = () => async dispatch => {
const data = await customerApi.getCustomer();
//dispatch seems to not be working
dispatch(slice.actions.getCustomer(data));
return data;
};
export default slice;
it is important to note that your component has not subscribed to the store yet. so if the customers object in the store is modified by another component then this component won't re-render, to fix this look up the useSelector hook from react-redux

TypeError: functionName is not a function (Redux - useDispatch)

I am getting TypeError: functionName is not a function when I call it with useDispatch. The initial state return normal as an empty array. This is my code:
Action:
export const getTopStories = () => async (dispatch) => {
try {
const { data } = await axios.get('http://news-site.com/api');
dispatch({
type: GET_TOP_STORIES,
payload: data,
});
} catch (err) {
console.log(err)
}
};
Reducer:
const initialState = {
topStories: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case GET_TOP_STORIES:
return { ...state, topStories: action.payload};
default:
return state;
}
};
Page:
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const TopStories = () => {
const stories = useSelector((state) => state.stories);
const { topStories, getTopStories } = stories;
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTopStories());
}, []);
You are trying to extract an action from the reducer state. If won't work like that. To get the state, you can use the useSelector hook like you are doing. But to use the action, you need to import that function and use it in the dispatch that you already have.
Something like this:
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getTopStories } from './action.js'; // Adjust the path
const TopStories = () => {
const stories = useSelector((state) => state.stories);
const { topStories } = stories;
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTopStories());
}, []);

Resources