I am trying to add products to shopping cart by storing them in local storage but, they are not getting stored. I looked into Redux-dev-tools and found out my state is not updating:
As you can see action is getting fired but my state is not updating:
Here is the source code:
cartAction.js
import axios from "axios"; import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const addToCart = (id, qty) => async (dispatch, getState) => { const { data } = await axios.get(`/api/product/${id}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
productID: data.product._id,
name: data.product.name,
image: data.product.image,
price: data.product.price,
countInStock: data.product.countInStock,
qty,
}, });
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems)); };
cartReducer.js
import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.state) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find(
(x) => x.productID === item.productID
);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.productID === existItem.productID ? item : x
),
};
} else {
return {
...state,
cartItems: [...state.cartItems, item],
};
}
// case CART_REMOVE_ITEM:
// return {};
default:
return state;
}
};
store.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
// reducers
import {
productDetailsReducer,
productListReducer,
} from "./reducers/productReducers";
import { cartReducer } from "./reducers/cartReducers";
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
});
const cartItemsFromStorage = localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [];
const initialState = {
cart: { cartItems: cartItemsFromStorage },
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
CartScreen.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addToCart, removeFromCart } from "../../redux/actions/cartActions";
import { Link } from "react-router-dom";
import ErrorMessage from "../../components/ErrorMessage/ErrorMessage";
import "./CartScreen.scss";
const CartScreen = ({ match, location, history }) => {
const productID = match.params.id;
const qty = location.search ? Number(location.search.split("=")[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
console.log(cartItems);
useEffect(() => {
if (productID) {
dispatch(addToCart(productID, qty));
}
}, [dispatch, productID, qty]);
return (
<>
<h1>Shopping Cart</h1>
</>
);
};
export default CartScreen;
You need to fix this on CartReducer.js
switch (action.state) {
to
switch (action.type) {
Related
I'm trying to use React Redux on my application. I will be building something like instagram, so the scale of the application is going to be big. I have implement redux correctly (I believe) although I'm not getting the fetch result from firestore on the dispatch function, if I insert the same code before the return it works.
action/index.ts:
import { db, auth } from '../../services/firebase';
import { USER_STATE_CHANGE } from '../constants';
export function fetchUser() {
return (dispatch: any) => {
db.collection('users')
.doc(auth.currentUser!.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
console.log(snapshot)
dispatch({
type: USER_STATE_CHANGE,
payload: snapshot.data(),
});
}
});
};
}
constants/index.ts:
export const USER_STATE_CHANGE = 'USER_STATE_CHANGE';
reducers/index.ts:
import { combineReducers } from 'redux';
import { user } from './user';
const Reducers = combineReducers({
userState: user,
});
export default Reducers;
reducers/user.ts:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
App.tsx:
// Redux
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './src/redux/reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default function App() {
if (!fontsLoaded) return <></>;
return (
<ThemeProvider theme={theme}>
<Provider store={store}>
<Main />
</Provider>
<ToastMessage />
</ThemeProvider>
);
}
Main.tsx:
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { AuthRoutes } from './routes/auth.routes';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchUser } from './redux/actions';
import { auth } from './services/firebase';
function Main() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
fetchUser();
auth.onAuthStateChanged((user) => {
if (user) setIsAuthenticated(true);
});
}, []);
return (
<NavigationContainer>
<AuthRoutes isAuthenticated={isAuthenticated} setIsAuthenticated={setIsAuthenticated} />
<StatusBar style="auto" />
</NavigationContainer>
);
}
const mapDispatchProps = (dispatch: any) => bindActionCreators({ fetchUser }, dispatch);
export default connect(null, mapDispatchProps)(Main);
The fetchUser() is called here, and should log the data from firestore, although I don't get any data logged.
The function is beeing called:
on reducer/user.ts
Have you try to do this:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Replace this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
With this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Because you are setting data in payload but you are accessing from currentUser which empty. Either replace payload with currentUser in useDispatch or replace currentUser with payload in reducer.
I am trying to store users fetched by random users api and trying to dispatch it to my store i have store users with an empty array, when i am trying to get users using useSelector i am getting an undefined object.
Here is my store.js:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import userReducer from "../features/users/userSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
Here is userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "users",
initialState: {
userArray: [],
},
reducers: {
getUsers: (state, action) => {
state.userArray = action.payload;
}
}
})
export const { getUsers } = userSlice.actions;
export const selectUsers = (state) => state.users.userArray;
export default userSlice.reducer;
Here is App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch } from 'react-redux';
import { getUsers, selectUsers } from './features/users/userSlice';
function App() {
const dispatch = useDispatch();
const users = useSelector(selectUsers);
const fetchUsers = async () => {
fetch("https://gorest.co.in/public/v2/users")
.then((response) => response.json())
.then((data) => {
console.log("data=====", data);
dispatch(getUsers(data));
});
};
return (
<div className="App">
<header className="App-header">
<button onClick={fetchUsers}>Get Users</button>
{users.length > 0 &&
users.map((user) => {
<li>user.name</li>;
})}
</header>
</div>
);
}
export default App;
you are mixing up state.user and state.users
either rename user in configureStore to users or use state.user.userArray
I'm using React and Redux for creating a shop website. I want to add items to the cart using redux, but cart items it's not getting added only showing an error:
GET http://localhost:3000/api/v1/product/61db003374fe4392e3446ae3 404 (Not Found)
"Uncaught (in promise) Error: Request failed with status code 404"
and I suppose the problem is maybe with ID in action but I'm not sure how to fix it.
Please help me out. Below is my code
// reducer
import { ADD_TO_CART } from '../constants/cartConstants';
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case ADD_TO_CART:
const item = action.payload;
const isItemExist = state.cartItems.find(
(i) => i.product === item.product
);
if (isItemExist) {
return {
...state,
cartItems: state.cartItems.map((i) =>
i.product === isItemExist.product ? item : i
),
};
} else {
return {
...state,
cartItems: [...state.cartItems, item],
};
}
default:
return state;
}
};
// actions
import axios from 'axios';
import { ADD_TO_CART } from '../constants/cartConstants';
export const addItemToCart = (id, quantity) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/v1/product/${id}`);
dispatch({
type: ADD_TO_CART,
payload: {
product: data.product._id,
name: data.product.name,
price: data.product.price,
image: data.product.images[0].url,
stock: data.product.stock,
quantity,
},
});
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
};
// store
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import {
productsReducer,
productsDetailsReducer,
} from './reducers/productReducers';
import {
authReducer,
userReducer,
forgotPasswordReducer,
} from './reducers/userReducers';
import { cartReducer } from './reducers/cartReducers';
const reducer = combineReducers({
products: productsReducer,
productDetails: productsDetailsReducer,
auth: authReducer,
user: userReducer,
forgotPassword: forgotPasswordReducer,
cart: cartReducer,
});
let initialState = {
cart: {
cartItems: localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [],
},
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
I've looked on other posts and it seemed to be a naming export thing, but I looked over and over my code and can't figure it out. I will appreciate any advice.
this is the error I'm getting:
TypeError: (0 , _redux_wetherSlice__WEBPACK_IMPORTED_MODULE_2__.setLocalLatitude) is not a function
at localWether.js:14
slice component
import { createSlice } from "#reduxjs/toolkit";
export const wetherSlice = createSlice({
name: "wether",
initialState: {
localLocation: {
latitude: "",
longitude: "",
},
reducers: {
setLocalLatitude: (state, action) => {
state.localLocation.latitude = action.payload;
},
setLocalLongitude: (state, action) => {
state.localLocation.longitude = action.payload;
},
},
},
});
export const { setLocalLongitude, setLocalLatitude } = wetherSlice.actions;
export default wetherSlice.reducer;
react component
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setLocalLatitude, setLocalLongitude } from "../../redux/wetherSlice";
const LocalWether = () => {
const dispatch = useDispatch();
const { localLocation } = useSelector((state) => state.wether);
useEffect(() => {
navigator.geolocation.getCurrentPosition((position) => {
try {
console.log(position);
dispatch(setLocalLatitude(position.coords.latitude));
dispatch(setLocalLongitude(position.coords.longitude));
} catch (err) {
console.log(err);
}
});
}, []);
console.log(localLocation);
return <button>Click For Location</button>;
};
export default LocalWether;
store
import { configureStore } from "#reduxjs/toolkit";
import wetherReducer from "../redux/wetherSlice";
const store = configureStore({
reducer: { wether: wetherReducer },
});
export default store;
i used react 16+ and redux get jsonplaceholder fake data to assign posts state but not working. can't assign the state. how can i assign json values into state using concat method. i check lifecycle methods also but can't get the answer.
Reducer
import * as actiontypes from './actions';
import axios from 'axios';
const initalstate = {
counter: 0,
posts: []
};
const reducer = (state = initalstate, action ) => {
switch (action.type) {
case actiontypes.actionFetchPost:
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return {
...state,
posts: state.posts.concat(res.data)
}
});
break;
default :
return state;
}
};
export default reducer;
Redux reducers must be pure functions, it means they should not contain any side effects like calling api.
You need to call api in action creators using redux-thunk package.
Codesandbox
An example action creator:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_FAILURE,
FETCH_POSTS_SUCCESS
} from "./actionTypes";
import axios from "axios";
export const fetchPosts = () => {
return dispatch => {
dispatch(fetchPostsStarted());
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
dispatch(fetchPostsSuccess(res.data));
})
.catch(err => {
dispatch(fetchPostsFailed(err.message));
});
};
};
const fetchPostsStarted = () => {
return {
type: FETCH_POSTS_STARTED,
payload: {
isLoading: true
}
};
};
const fetchPostsSuccess = posts => {
return {
type: FETCH_POSTS_SUCCESS,
payload: {
posts
}
};
};
const fetchPostsFailed = error => {
return {
type: FETCH_POSTS_FAILURE,
payload: {
error
}
};
};
And reducer file:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from "../actions/actionTypes";
const initialState = {
posts: [],
loading: false,
error: null
};
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_POSTS_STARTED:
return {
...state,
loading: true
};
case FETCH_POSTS_SUCCESS:
return {
...state,
loading: false,
error: null,
posts: action.payload.posts
};
case FETCH_POSTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
In store we use redux-thunk like this:
import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import reduxThunk from "redux-thunk";
import postsReducers from "./reducers/postsReducers";
const rootReducer = combineReducers({
posts: postsReducers
});
const store = createStore(rootReducer, compose(applyMiddleware(reduxThunk)));
export default store;
Posts component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "./store/actions/postsActions";
class Posts extends Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
const { posts, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
const { posts, loading, error } = state.posts;
return {
posts,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchPosts
}
)(Posts);
Index.js
import ReactDOM from "react-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import Posts from "./Posts";
function App() {
return (
<div className="App">
<Posts />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
Do api call in action and return promise, use redux-thunk and redux-promise-middleware:
export const myApiCall = (args1, arg2) => async (dispatch, getState) => {
const payload = fetch({ ...config });
return dispatch({ type: 'MY_API_CALL', payload });
}
Then in reducer will have to handle two results:
MY_API_CALL_FULFILLED and MY_API_CALL_REJECTED