Im new to react and redux. After following a tutorial and doing everything wrong (he was using react-router-dom 5.2 and I was using 6) I decided to do it like him and use react-router-dom 5.2. Now for some reason the redux part of the project doesn't work. I don't even get an error. It just doesn't show in the redux dev tools. I think the frontend isn't connecting to the backend properly (Django). Maybe someone has an idea?
Homescreen:
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col } from 'react-bootstrap'
import Product from '../components/Product'
import Loader from '../components/Loader'
import Message from '../components/Message'
import { listProducts } from '../actions/productActions'
function HomeScreen() {
const dispatch = useDispatch()
const productList = useSelector(state => state.productList)
const { error, loading, products} = productList
//let keyword = history.location.search
useEffect(() => {
dispatch(listProducts())
}, [dispatch])
productReducers.js
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
} from '../constants/productConstants'
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return { loading: true, products: [] }
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
products: action.payload
}
case PRODUCT_LIST_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
}
productActions.js
import axios from 'axios'
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
} from '../constants/productConstants'
export const listProducts = () => async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST })
const { data } = await axios.get('/api/products/')
dispatch({
type: PRODUCT_LIST_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
store.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import {productListReducer} from './reducers/productReducers'
const reducer = combineReducers({
productList: productListReducer,
})
const initialState = {}
const middleware = [thunk]
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware)))
export default store
My redux dev tool in browser is empty and there are no errors on the console. Thank you!
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
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
I'm new to React, Redux and have been following tutorials on the topic. I'm come across a lot of issues that I've been able to resolve but I've come across an issue I can't resolve. I set up store, and can even view it through Chrome's Redux Tools and it show correctly, however when I try and dispatch to the Store, I always get a Cannot read property 'dispatch' of undefined error. I have followed numerous tutorials letter for letter, and am still stuck with the same error message.
Index.Js
import Layout from '../components/layout/Layout';
import Home from '../components/Home';
import { getRooms } from '../redux/actions/roomActions';
import { wrapper } from '../redux/store';
export default function Index() {
return (
<Layout>
<Home />
</Layout>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
async ({ req, store }) => {
await store.dispatch(getRooms(req));
}
);
roomConstants.js
export const ALL_ROOMS_SUCCESS = 'ALL_ROOMS_SUCCESS';
export const ALL_ROOMS_FAIL = 'ALL_ROOMS_FAIL';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
reducer.js
import { combineReducers } from 'redux';
import { allRoomsReducer } from './roomReducers';
const reducer = combineReducers({
allRooms: allRoomsReducer,
});
export default reducer;
Store.js
import { createStore, applyMiddleware } from 'redux';
import { HYDRATE, createWrapper } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import reducers from './reducers/reducers';
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
};
return nextState;
} else {
return reducers(state, action);
}
};
const initStore = () => {
return createStore(reducer, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore);
roomReducer.js
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
// All rooms reducer
export const allRoomsReducer = (state = { rooms: [] }, action) => {
switch (action.type) {
case ALL_ROOMS_SUCCESS:
return {
roomsCount: action.payload.roomsCount,
resPerPage: action.payload.resPerPage,
filteredRoomsCount: action.payload.filteredRoomsCount,
rooms: action.payload.rooms,
};
case ALL_ROOMS_FAIL:
return {
error: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
roomAcion.js
import axios from 'axios';
import absoluteUrl from 'next-absolute-url';
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
//Clear errors
export const clearErrors = () => async (dispatch) => {
return dispatch({
type: CLEAR_ERRORS,
});
};
// Get all rooms
export const getRooms = (req) => async (dispatch) => {
try {
const { origin } = absoluteUrl(req);
const { data } = await axios.get(`${origin}/api/rooms`);
dispatch({
type: ALL_ROOMS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_ROOMS_FAIL,
payload: error.response.data.message,
});
}
};
in index.js, your getServerSideProps function should read this;
export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => {
await store.dispatch(getRooms(req));})
Use the old version of next-redux-wrapper like 6.0.2
I am studying redux-saga and I want to fetch data from :
https://jsonplaceholder.typicode.com/posts
and in my redux folder I have the fallowing:
(it can be checked in this github repository
https://github.com/jotasenator/redux-saga-fetching-example/tree/main/src)
\src\redux\api.js
import axios from 'axios'
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
}
the get request to the address in question
src\redux\app.actions.js
export const loadPostStart = () => ({
type: 'LOAD_POST_START',
})
export const loadPostSuccess = (posts) => ({
type: 'LOAD_POST_SUCCESS',
payload: posts,
})
export const loadPostFail = (error) => ({
type: 'LOAD_POST_FAIL',
payload: error,
})
those are the actions functions
src\redux\app.reducer.js
const INITIAL_STATE = {
loading: false,
posts: [],
errors: null,
}
export const appReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'LOAD_POST_START':
return {
...state,
loading: true,
}
case 'LOAD_POST_SUCCESS':
return {
...state,
posts: action.payload,
loading: false,
}
case 'LOAD_POST_FAIL':
return {
...state,
errors: action.payload,
loading: false,
}
default:
return state;
}
}
the reducer of the fetching, updating state,
src\redux\counterReducer.js
import { types } from "./types";
const initialState = {
value: 0
}
export const counterReducer = (state = initialState, action) => {
switch (action.type) {
case types.adicionar:
return {
...state,
value: state.value + 1
}
case types.resetear:
return {
...state,
value: 0
}
case types.restar:
return {
...state,
value: state.value - 1
}
default:
return state
}
}
this is the reducer of the counter app, with different approach, types are isolated in another file
src\redux\rootReducer.js
import { combineReducers } from 'redux'
import { counterReducer } from './counterReducer'
import { appReducer } from './app.reducer'
export const rootReducer = combineReducers({
counterReducer,
appReducer
})
the rootReducer for gathering the reducers
src\redux\sagas.js
import { put, takeLatest, call } from 'redux-saga/effects'
import { loadPostApi } from './api'
import { loadPostFail, loadPostSuccess } from './app.actions'
export function* onLoadPostStartAsync() {
try {
const response = yield call(loadPostApi)
yield put(loadPostSuccess(response.data))
} catch (error) {
yield put(loadPostFail(error))
}
}
export function* onLoadPost() {
yield takeLatest('LOAD_POST_START', onLoadPostStartAsync)
}
export default function* rootSaga() {
yield ([
onLoadPost(),
])
}
saga onLoadPostStartAsync called by saga onLoadPost inside rootSaga
src\redux\store.js
import { applyMiddleware, compose, createStore } from "redux";
import createSagaMiddleware from 'redux-saga'
import { rootReducer } from "./rootReducer";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
export const store = createStore(rootReducer, enhancer)
sagaMiddleware.run(rootSaga)
this is the store with the redux_devtool_extension, the reducers, and running rootSaga
src\redux\types.js
export const types = {
adicionar: 'ADICIONAR',
resetear: 'RESETEAR',
restar: 'RESTAR'
}
those are the types of the counterApp reducer
src\Counter.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export const Counter = () => {
const dispatch = useDispatch()
const { value } = useSelector(state => state.counterReducer)
const handleAdicionar = () => {
dispatch({ type: 'ADICIONAR' })
}
const handleResetear = () => {
(value !== 0) && dispatch({ type: 'RESETEAR' })
}
const handleRestar = () => {
dispatch({ type: 'RESTAR' })
}
console.log(value)
return (
<div>
<button onClick={handleAdicionar}>Adicionar</button>
{' '}
<button onClick={handleResetear}>Resetear</button>
{' '}
<button onClick={handleRestar}>Restar</button>
<hr />
</div>
)
}
this is the Counter component, it works ok
src\Fetching.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { loadPostStart } from './redux/app.actions'
export const Fetching = () => {
const dispatch = useDispatch()
const fetchPost = () => {
dispatch(loadPostStart())
}
const state = useSelector(state => state.appReducer)
console.log(state)
return (
<>
<h1>Fetching from https://jsonplaceholder.typicode.com</h1>
<button onClick={fetchPost}>Fetching</button>
{
!state.loading && state.posts.map((post) => (
<li key={post.id}><h2>{post.title}</h2></li>
))
}
</>
)
}
the Fetching component click on the button calls fetchPost function who dispatch loadPostStart() function which is the same of dispatching {type: 'LOAD_POST_START'}, but nothing happens here when clicking, not fetch nothing from here https://jsonplaceholder.typicode.com/posts
src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { store } from './redux/store';
import { Provider } from "react-redux";
import { Unificator } from './Unificator';
ReactDOM.render(
<Provider store={store}>
<Unificator />
</Provider>,
document.getElementById('root')
);
component Unificator has Counter and Fetching component
src\Unificator.js
import React from 'react'
import { Counter } from './Counter'
import { Fetching } from './Fetching'
export const Unificator = () => {
return (
<div>
<Counter />
<Fetching />
</div>
)
}
as you can see is about of two reducers, one is the famous counter, and the another one is the fetching issue, do not know what is happening that is not fetching the data
obviously, i am doing something wrong here...don t see where
Axio returns promise, You need to capture that and return. Please try replacing below code.
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then((response) => {
console.log('Response', response);
return response;
})
.catch((error) => {
console.log('error', error);
})
}
I'm learning redux and I'm trying to pull out values from state using useSelector hook and I really don't know why I cant see my error and loading property from state which is inside user obj. I'm also using initial state in store and when I try console log userInfo, error and loading I can see only userInfo and not loading and error. Is that initial state in store causing this problem? please help me out ..thank you
my code
login.js
import React, {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {loginUser, logoutUser} from '../../actions/userAction';
import {alert} from '../../actions/alertAction';
const Login = (props) => {
const dispatch = useDispatch();
const user= useSelector(state => state.user)
const alertMsg = useSelector(state => state.alert)
**console.log(user)**
**const {userInfo, loading, error} = user**
**console.log(userInfo, loading, error)**
return ("<h1>welcome to login page")
}
userAction.js file
import {USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS,USER_LOGIN_ERROR, USER_REGISTER_REQUEST, USER_REGISTER_SUCCESS, USER_LOGOUT_SUCCESS} from '../types';
import axios from 'axios';
export const loginUser = (email, password) => async(dispatch) => {
try {
console.log('login user')
dispatch({
type: USER_LOGIN_REQUEST,
});
const config = {
headers: {
"Content-Type": "application/json",
},
};
const {data} = await axios.post(
"/user/login",
{ email, password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
}catch(error) {
console.log(error)
dispatch({
type: USER_LOGIN_ERROR,
payload: error.response.data.msg
})
}
}
userReducer.js file
import {USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS, USER_REGISTER_REQUEST,USER_LOGIN_ERROR, USER_REGISTER_SUCCESS, USER_LOGOUT_SUCCESS} from '../types';
export default (state = {}, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return {
...state,
user: {loading: true}
};
case USER_LOGIN_SUCCESS:
return {
...state,
user: {
userInfo: action.payload, loading: false
}
}
case USER_LOGIN_ERROR:
return {
...state,
user: {
loading: false, error: action.payload
}
}
case USER_LOGOUT_SUCCESS:
return {
...state
};
default:
return state
}
}
index.js file
import {combineReducers} from 'redux';
import cartReducer from './cartReducer';
import userReducer from './userReducer';
export default combineReducers({
cart: cartReducer,
user: userReducer
})
store.js file
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index.js';
const cartItemsFromStorage = localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : []
const userInfoFromStorage = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : null
const initialState = {
**cart: {
cartItems: cartItemsFromStorage
},
user: {
userInfo: userInfoFromStorage
}**
};
const middleware = [thunk];
const store = createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(...middleware)));
export default store;
Problem is in how you are storing data in redux.
case USER_LOGIN_REQUEST:
return {
...state,
loading: true, // Here you are storing loading in redux state directly
error: null // same as loading
};
To solve this you need to store like below:-
case USER_LOGIN_REQUEST:
return {
...state,
user: { ...state.user, loading: true, error: null}
};
You also need to change this for all your cases where you are trying to store loading and error in user
I'm using Redux for the first time and I'm getting this error on the redux devtools:
error: "Actions must be plain objects. Use custom middleware for async actions."
This is my store.js code:
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {productListReducer} from './reducers/productReducers';
const initialState = {};
const reducer = combineReducers ( {
productList: productListReducer,
})
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
And the productReducers.js where I have the switch for PRODUCT_LIST_FAIL:
import { PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_LIST_FAIL } from "../constants/productConstants";
function productListReducer(state = {products:[]}, action) {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return {loading: false, products: action.payload};
case PRODUCT_LIST_FAIL:
return {loading: false, error: action.payload};
default:
return state;
}
}
export {productListReducer};
The import from the last code comes from here (productConstants.js):
export const PRODUCT_LIST_REQUEST = 'PRODUCT_LIST_REQUEST';
export const PRODUCT_LIST_SUCCESS = 'PRODUCT_LIST_SUCCESS';
export const PRODUCT_LIST_FAIL = 'PRODUCT_LIST_FAIL';
productActions.js:
import axios from 'axios';
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
} from '../constants/productConstants';
const listProducts = () => async(dispatch) => {
try {
dispatch(PRODUCT_LIST_REQUEST);
const {data} = await axios.get("/api/products");
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
}
catch(error) {
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
export {listProducts}
guys.
My bad, I was writing dispatch(PRODUCT_LIST_REQUEST); instead of dispatch({type: PRODUCT_LIST_REQUEST}); inside the try-catch in productActions.js, that's why the PRODUCT_LIST_REQUEST was assumed as a string and not an object...