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...
Related
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!
I am having this error in react-redux. I don't know how to solve it. I wanted to send a param which is taken from an api to another api and fetch results.
This is my store
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from "./Reducers/rootReducer";
const store = createStore(
rootReducer,
{},
compose(applyMiddleware(thunk), composeWithDevTools())
);
export default store;
This is my reducer code:
import * as types from "../Actions/types";
const initialState = {
posts: [],
table: [],
};
const postReducer = (state = initialState, action) => {
switch (action.type) {
case types.FETCH_DATA:
return {
...state,
posts: action.payload,
};
case types.FETCH_TABLE:
return {
...state,
table: action.payload,
};
default:
return {
...state,
};
}
};
export default postReducer;
This is my action code
export const getData = (from_userpart) => async (dispatch) => {
try {
const { data } = await api.getData(from_userpart);
dispatch({
type: types.FETCH_TABLE,
payload: data,
});
} catch (error) {
console.log(error);
}
};
When i use this code on the parent component,it works but i want to use that in my child component but it gives this error as i mentioned on the label. How to solve that?
useEffect(() => {
dispatch(getData(phone));
}, [dispatch]);
Change the setting of your store, your enhancer should come second.
let composeEnhancers = null
if (process.env.NODE_ENV === 'development') {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
} else {
composeEnhancers = compose
}
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(thunk))
);
I've noticed that i had imported my function from api folder not the action one. It works now
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 trying to combine reduxForm with my custom reducers inside nextjs with no luck. I'm using this as initial working example: https://github.com/zeit/next.js/blob/master/examples/with-redux-wrapper/store.js
When I add reduxForm according to their docs, I get error: Cannot read property 'source' of undefined , which means that store doesn't even exist.
How my store.js looks now:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { reducer as formReducer } from 'redux-form';
import thunkMiddleware from 'redux-thunk';
import axios from 'axios';
import getRootUrl from '../lib/api/getRootUrl';
const initialState = {
user: 0,
agent: 0,
};
export const actionTypes = {
FETCH_USER: 'FETCH_USER',
USER_AGENT: 'USER_AGENT',
};
// REDUCERS
const authReducer = (state = { user: 0 }, action) => {
switch (action.type) {
case 'FETCH_USER': return { user: action.payload };
default: return state;
}
};
const agentReducer = (state = { agent: 0 }, action) => {
switch (action.type) {
case 'USER_AGENT': return { agent: action.payload };
default: return state;
}
};
// ACTIONS
export const fetchUser = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resUser = await axios.get(`${ROOT_URL}/api/current_user`);
dispatch({ type: actionTypes.FETCH_USER, payload: resUser.data });
};
export const getUserAgent = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resAgent = await axios.get(`${ROOT_URL}/api/useragent`);
dispatch({ type: actionTypes.USER_AGENT, payload: resAgent.data });
};
const rootReducer = combineReducers({
authReducer,
agentReducer,
formReducer,
});
export const initStore = (newState = initialState) => createStore(
rootReducer,
newState,
compose(applyMiddleware(thunkMiddleware)),
);
Last working example. I tried to combine with-redux-wrapper syntax with reduxForm docs. reduxForm action and reducer don't work here: https://github.com/neone35/rearn/blob/master/server/store.js
How can I combine these two to use reduxForm inside my component containing Field components?
I solved my problem after reading more of this documentation:
https://redux.js.org/recipes/structuring-reducers/initializing-state
https://redux.js.org/recipes/structuring-reducers/using-combinereducers
Also, I've logged out console to see what props my component is receiving to resolve problem faster.
How my store.js looks now:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { reducer as formReducer } from 'redux-form';
import thunkMiddleware from 'redux-thunk';
import axios from 'axios';
import getRootUrl from '../lib/api/getRootUrl';
const initialState = {
user: '0',
agent: '0',
};
export const actionTypes = {
FETCH_USER: 'FETCH_USER',
USER_AGENT: 'USER_AGENT',
};
// REDUCERS
const authReducer = (state = null, action) => {
switch (action.type) {
case actionTypes.FETCH_USER:
return action.payload || false;
default:
return state;
}
};
const agentReducer = (state = null, action) => {
switch (action.type) {
case actionTypes.USER_AGENT:
return action.payload || false;
default:
return state;
}
};
export const rootReducer = combineReducers({
user: authReducer,
agent: agentReducer,
form: formReducer,
});
// ACTIONS
export const fetchUser = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resUser = await axios.get(`${ROOT_URL}/api/current_user`);
dispatch({ type: actionTypes.FETCH_USER, payload: resUser.data });
};
export const getUserAgent = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resAgent = await axios.get(`${ROOT_URL}/api/useragent`);
dispatch({ type: actionTypes.USER_AGENT, payload: resAgent.data });
};
export const initStore = (newInitialState = initialState) =>
createStore(rootReducer, newInitialState, compose(applyMiddleware(thunkMiddleware)));
I separated all my reducers into functions which initialize state to null (because initStore gets passed zeros, and preloadedState (initialState) always has priority). I return raw value from action creator (without object) and it gets passed straight to combineReducers (which creates object) which is rootReducer inside createStore.
I'm calling the action requestLoadOrders to fetch the orders I need. I'm dispatching with type: REQUEST and afterwards with SUCCESS or FAILURE. The fetch succeeded because my orders are in the payload in the redux dev-tools, but the action that I receive in my reducer is ##redux/PROBE_UNKNOWN_ACTION_z.r.p.l.z. I found a thread about this here, however I can't seem to find what I'm doing wrong?
actions.js
import {
LOAD_ORDERS_REQUEST,
LOAD_ORDERS_SUCCESS,
LOAD_ORDERS_FAILURE
} from './constants';
import { fetchOrders } from '../../api';
export const requestLoadOrders = () => {
return (dispatch, getState) => {
dispatch({ type: LOAD_ORDERS_REQUEST });
fetchOrders().then(orders => {
dispatch({ type: LOAD_ORDERS_SUCCESS, payload: orders });
}).catch(error => {
console.error(error);
dispatch({ type: LOAD_ORDERS_FAILURE, payload: error });
});
};
};
reducer.js
import {
LOAD_ORDERS_REQUEST,
LOAD_ORDERS_SUCCESS,
LOAD_ORDERS_FAILURE
} from './constants';
const initialState = {
orders: []
};
const orderReducer = ( state = initialState, { payload, type }) => {
switch (type) {
case LOAD_ORDERS_REQUEST :
return state;
case LOAD_ORDERS_SUCCESS :
return { ...state, orders: payload};
case LOAD_ORDERS_FAILURE :
return { ...state, error: payload.error};
default :
return state;
}
};
export default orderReducer;
My actions get dispatched correctly, but I suppose there's a problem with the reducer receiving its data. Therefor I also added my store and combined reducers files.
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import thunk from 'redux-thunk';
import makeRootReducer from './reducers';
export const history = createHistory();
const initialState = {}
const enhancers = [];
const middleware = [ routerMiddleware(history), thunk ];
if (process.env.NODE_ENV === 'development') {
const devToolsExtension = window.devToolsExtension;
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension());
}
}
const composedEnhancers = compose(
applyMiddleware(...middleware),
...enhancers
);
const store = createStore(
makeRootReducer,
initialState,
composedEnhancers
);
export default store;
reducers.js
import { combineReducers } from 'redux';
import orderReducer from '../modules/Order/reducer';
export const makeRootReducer = asyncReducers => {
return combineReducers({
order: orderReducer,
...asyncReducers
});
}
export default makeRootReducer;
I found my mistake. I should execute the makeRootReducer function by adding the brackets after the word in createStore().
Updated the createStore() part of store.js to:
const store = createStore(
makeRootReducer(),
initialState,
composedEnhancers
);
was the fix.