Getting an error that I need to use middleware, but I have already applied middleware - javascript

Okay so I am following a tutorial, and I'm a beginner. This is my first experience with Redux.
This is the error I've been getting when it should be displaying the home screen of my webpage.
Actions must be plain objects. Instead, the actual type was: 'string'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.
I have been searching everywhere but it looks to me like I applied thunk correctly. I'm hoping someone more experienced will be able to spot my mistake. Thank you.
HomeScreen.js
import React, { useEffect } from 'react';
import {Link} from 'react-router-dom';
import { listProducts } from '../actions/productActions.js';
import { useDispatch, useSelector } from 'react-redux';
function HomeScreen() {
const productList = useSelector(state => state.productList);
const { products, loading, error} = productList;
const dispatch = useDispatch();
useEffect(() => {
dispatch(listProducts());
return () => {
//
};
}, [])
return loading? <div>Loading...</div> :
error? <div>{error}</div>:
<ul className="products">
{
products.map(product =>
<li key={product._id}>
<div className="product">
<Link to={'/product/' + product._id}>
<img className="product-image" src={product.image} alt="product" />
</Link>
<div className="product-name">
<Link to={'/product/' + product._id}>{product.name}</Link>
</div>
<div className="product-brand">{product.brand}</div>
<div className="product-price">${product.price}</div>
<div className="product-rating">{product.rating} Stars ({product.numReviews} Reviews)</div>
</div>
</li>)
}
</ul>
}
export default HomeScreen;
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { productListReducer } from './reducers/productReducers.js';
import thunk from 'redux-thunk';
import * as compose from 'lodash.flowright';
const initialState = {};
const reducer = combineReducers({
productList: productListReducer,
})
const composeEnhancer = window.__REDUXDEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
productActions.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from "../constants/productconstants.js";
import axios from "axios";
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};
productReducers.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } 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 }

PRODUCT_LIST_REQUEST appears to be a string. You cannot dispatch a string by itself - only action objects. Actions are always objects that have a type field inside, like {type: 'counter/incremented'}.
That said, you should be using our official Redux Toolkit package to write your Redux code. Redux Toolkit will simplify all of the Redux store setup and reducer logic you've shown.

Related

react-redux state is changing but display is stills same (no re-render)

i've been working on a project trying to learn redux with react. But there is an error and i don't know exactly how to fix it. Files/codes in down if you need more information about how store works.
store/index.js
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "./reducers";
import thunk from 'redux-thunk'
const middlewares = [thunk]
const store = createStore(rootReducer, compose(applyMiddleware(...middlewares), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))
export default store;
actions/index.js
import axios from "axios"
export const increment = (payload) => {
return {
type: 'INCREMENT',
payload: payload
}
}
export const decrement = () => {
return {
type: 'DECREMENT'
}
}
export const fetch = () => {
return async (dispatch) => {
axios.get('https://jsonplaceholder.typicode.com/posts/')
.then(data => dispatch({type: 'FETCH', payload: data.data}))
}
}
store/todos.js
const todos = (state = [], action) => {
switch(action.type){
case 'FETCH':
return Object.assign(state, action.payload);
default:
return state;
}
}
export default todos;
App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch, connect} from 'react-redux'
import {increment, decrement, fetch} from './store/actions/'
import { GeistProvider, CssBaseline } from '#geist-ui/react';
function App(props) {
const count = useSelector((state) => state.counter)
const todos = useSelector((state) => state.todos)
const dispatch = useDispatch()
return (
<div className="App">
<header className="App-header">
<h1>Count is {count}</h1>
<button onClick={() => dispatch(increment(3))}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(fetch())}>FETCH</button>
{todos.length ? todos[0].title : <h1>Not fetched.</h1>}
</header>
</div>
);
}
export default App;
This is the codes in project. Let me know if you guys need more information about anything. Thanks for help!
You are wrong at return Object.assign(state, action.payload);. It's mutated state so redux can't detect state change. You should read this https://redux.js.org/understanding/thinking-in-redux/three-principles#changes-are-made-with-pure-functions
You can change to this
return Object.assign({}, state, action.payload);
or this
return { ...state, ...action.payload }

React : Action Must Be Plain Object

I have below project structure -
I have GlobalStore.js where I have below code:-
import React from 'react'
const GlobalContext=React.createContext();
const GlobalProvider=GlobalContext.Provider;
const GlobalConsumer=GlobalContext.Consumer;
export {GlobalProvider,GlobalConsumer}
LoginReducers/Login_Action.js with below code -
const VERIFY_CREDENTIALS ='VERIFY_CREDENTIALS'
export function VerifyCredentials()
{
return{
type :VERIFY_CREDENTIALS
}
}
LoginReducers/Login_Reducers.js with below code -
import Axios from "axios";
import { VerifyCredentials } from "./Login_Action";
const initialState={
userName:"",
password:"",
isVarified:false
}
const url='http://localhost:52016/api/values/';
export const LoginReducer=(state=initialState,action)=>{
switch (action.type) {
case 'VERIFY_CREDENTIALS':
Axios.get(url)
.then(x=>{
alert(x.data);
})
default:
break;
}
}
GlobalStorage/store.js with below code -
import { createStore } from 'redux';
import { LoginReducer } from "../Components/LoginReducers/Login_Reducers";
export const store=createStore(LoginReducer);
App.js with below code -
import logo from './logo.svg';
import './App.css';
import Login from './Components/Login';
import { store } from "./GlobalStorage/store";
import {GlobalProvider,GlobalConsumer} from "./GlobalStore";
function App() {
return (
<div className="App">
<GlobalProvider value={store}>
<Login></Login>
</GlobalProvider>
</div>
);
}
export default App;
I am getting below error:-
Please suggest what changes can be made in order to resolve this error?
Also please suggest if above code structure is recommended or not where I am sharing store through GlobalProvider.
Also your reducer should be pure function and do your staff on action like this :
Action
import Axios from "axios";
const VERIFY_CREDENTIALS ='VERIFY_CREDENTIALS';
const ERROR_CREDENTIALS='ERROR_CREDENTIALS';
export function VerifyCredentials(username,password)
{
return Axios.post(url,{username,password})
.then(x=>{
{type :VERIFY_CREDENTIALS,payload:{userData:x.data, isVarified:true} }
})
.catch((err) => {type :ERROR_CREDENTIALS});
}
Reducer
import { VERIFY_CREDENTIALS ,ERROR_CREDENTIALS} from "./Login_Action";
const initialState={
userData:"",
isVarified:false
}
const url='http://localhost:52016/api/values/';
export const LoginReducer=(state=initialState,action)=>{
switch (action.type) {
case VERIFY_CREDENTIALS:
return action.payload;
case ERROR_CREDENTIALS:
return state;
default:
return state;
}
}
I installed -
npm install --save redux-thunk
Also added below code in store.js -
import { applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
export const store=createStore(LoginReducer,applyMiddleware(thunk));
Url which helped me -
https://www.npmjs.com/package/redux-thunk
Credit For Solution -
Nilesh Patel's Comment
You are very wrong in your code !!!
Please note all of the following. Please also read the documentation - for write better code
use react-redux library - and for create async actions use redux-thunk
import {Provider} from "react-redux"; // you need this
import logo from './logo.svg';
import './App.css';
import Login from './Components/Login';
import { store } from "./GlobalStorage/store";
function App() {
return (
<div className="App">
<Provider store={store}>
<Login/>
</Provider>
</div>
);
}
export default App;
Login_Reducers.js
const initialState={
userName:"",
password:"",
isVarified:false,
loading:false,
error:false,
}
export const LoginReducer=(state=initialState,action)=>{
switch (action.type) {
case 'VERIFY_CREDENTIALS':
// return new state
alert(action.payload)
return {
...state,
username:"John",
password:'12345678'
}
case "VERIFY_CREDENTIALS_LOADING":
return{
...state,
loading:true,
}
case "VERIFY_CREDENTIALS_ERROR":
return{
...state,
error:true,
}
default:
return state
}
}
Login_Action.js
const VerifyCredentialsError = ()=>({type:"VERIFY_CREDENTIALS_ERROR"})
const VerifyCredentialsLoading = ()=>({type:"VERIFY_CREDENTIALS_LOADING"})
const VerifyCredentialsSuccess = (data)=>({type:"VERIFY_CREDENTIALS",payload:data})
export const RequestVerifyCredentials = ()=>{
return(dispatch)=>{
dispatch(VerifyCredentialsLoading())
Axios.get(url)
.then(x=>{
alert(x.data);
dispatch(VerifyCredentialsSuccess(x.data))
})
.catch(err => dispatch(VerifyCredentialsError()))
}
}
Login.js
import {useEffect} from 'react'
import {useDispatch} from 'react-redux'
import {RequestVerifyCredentials} from 'Login_Action.js'
const Login = ()=>{
const dispatch = useDispatch()
useEffect(()=>{
dispatch(RequestVerifyCredentials())
},[dispatch])
return(
<h2>Login Component</h2>
)
}
react-redux Provider DOCS:
https://react-redux.js.org/api/provider
react-redux Hook DOCS:
https://react-redux.js.org/api/hooks
redux-thunk github repository and docs:
https://github.com/reduxjs/redux-thunk

Using redux with react-hooks without the connect HOC

Currently, I'm using functional components with hooks but still dispatching my actions with the connect HOC.
I read through the documentation with useDispatch but I'm unsure how to incorporate it in my code. From the examples, they are passing the the action types and payloads inside the component. Would I have to move myOfferActions functions back to the component in order to useDispatch?
MyOffers component
import React, { useEffect } from "react";
import { connect, useSelector } from "react-redux";
import "./MyOffers.scss";
import MyOfferCard from "../../components/MyOfferCard/MyOfferCard";
import { fetchMyOffers } from "../../store/actions/myOffersActions";
const MyOffers = (props) => {
const myOffers = useSelector((state) => state.myOffers.myOffers);
useEffect(() => {
props.fetchMyOffers();
}, []);
return (
<div className="my-offers-main">
<h1>My offers</h1>
{myOffers && (
<div className="my-offer-container">
{myOffers.map((offer) => (
<MyOfferCard key={offer.id} offer={offer} />
))}
</div>
)}
</div>
);
};
export default connect(null, { fetchMyOffers })(MyOffers);
offerActions
export const fetchMyOffers = () => async (dispatch) => {
const userId = localStorage.getItem("userId");
try {
const result = await axiosWithAuth().get(`/offers/${userId}`);
let updatedData = result.data.map((offer) => {
//doing some stuff
};
});
dispatch(updateAction(FETCH_MY_OFFERS, updatedData));
} catch (error) {
console.log(error);
}
};
offerReducer
import * as types from "../actions/myOffersActions";
const initialState = {
offerForm: {},
myOffers: [],
};
function myOffersReducer(state = initialState, action) {
switch (action.type) {
case types.FETCH_MY_OFFERS:
return {
...state,
myOffers: action.payload,
};
default:
return state;
}
}
export default myOffersReducer;
I don't think you need connect when using the redux hooks.
You just need to call useDispatch like:
const dispatch = useDispatch();
And use the function by providing the object identifying the action:
dispatch({ type: 'SOME_ACTION', payload: 'my payload'});
It should be working with redux-thunk too (I guess this is what you're using): dispatch(fetchMyOffers())

Actions Must be plain objects. Use Custom middleware for async actions when I already have thunk

I have thunk middleware applied but I am still getting impure error. I would greatly appreciate any help.
Where I call the creator
import React from 'react'
import { StyleSheet, Text, View, Button } from 'react-native'
import { shallowEqual, useSelector, useDispatch } from 'react-redux'
import { signIn } from '../../redux/actions/user'
const LogInOrSignUpScreen = () => {
const user = useSelector(state => state.user, shallowEqual)
return (
<View>
<Text>Login Or Sign up</Text>
<Button title='clicked' onPress={useDispatch(signIn('ok', 'dal'))} />
</View>
)
}
export default LogInOrSignUpScreen
Reducer
export const userReducer = (state = { ...initState.user }, action) => {
switch (action.type) {
case SIGN_IN_ERROR:
return state;
// case SIGN_IN_PENDING:
// return state;
case SIGN_IN_SUCCESS:
return { ...state, ...action.data }
default:
//console.log(state)
return state;
}
}
Async Action Creator:
export function signIn(userId, pass) {
let creditials = { userId, pass }
return async (dispatch) => {
dispatch(signInPending())
try {
console.log('clicked')
} catch (e) {
console.log(e);
dispatch(signInError());
// return;
}
dispatch(signInSuccess(creditials))
}
}
Configure Store
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk'
import { initialState } from './dataModel'
import rootReducer from './reducers'
export function configureStore() {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunkMiddleware)
)
}
export default configureStore;
App.js
const store = configureStore()
const Stack = createStackNavigator();
<Provider store={store}>
<NavigationContainer>
{isUserLoggedIn()}
</NavigationContainer>
</Provider>
)
The text below is the fulfill the requirement of me having more filler info about the question I do apologize.
I have thunk middleware applied but I am still getting impure error. I would greatly appreciate any help.
const dispatch = useDispatch()
I was just using the raw useDispatch function... instead of storing the instance in a variable.....
thanks for looking over my problem and I hope this helps someone else as well.

ReactJS: Redux state is not changing

I'm just starting with React and Redux and stumbled upon something I can't figure out by myself - I think that Redux state is not changing and it's causing (some of) errors. I'm checking state with use of remote-redux-devtools#0.5.0.
My code:
Categories.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getCategories } from '../../actions/categories';
export class Categories extends Component {
static propTypes = {
categories: PropTypes.array.isRequired
};
componentDidMount() {
this.props.getCategories();
}
render() {
return (
<div>
Placeholder for categories.
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories.categories
});
export default connect(mapStateToProps, { getCategories })(Categories);
../../actions/categories.js:
import axios from "axios";
import { CATEGORIES_GET } from "./types";
export const getCategories = () => dispatch => {
return axios
.get("/api/notes/categories/")
.then(res => {
dispatch({
type: CATEGORIES_GET,
payload: res.data
});
})
.catch(err => console.log(err));
};
reducers/categories.js:
import { CATEGORIES_GET } from '../actions/types.js';
const initialState = {
categories: []
};
export default function (state = initialState, action) {
switch (action.type) {
case CATEGORIES_GET:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
store.js:
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'remote-redux-devtools';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware)));
export default store;
reducers/index.js
import { combineReducers } from "redux";
import categories from './categories';
export default combineReducers({
categories,
});
Using remote-redux-devtools, I've never seen anything in my state. Currently this code above gives me 3 errors, two of them being
this.props.getCategories is not a function
My guess is that because there is some issue with Categories class, it's not passing anything to state and it could be root cause of errors. I had one more error, connected to Categories not being called with attributes, but for debug purposes I put empty array there - one error dissapeared, but that's it. I've also tried adding constructor to Categories and called super(), but did not help also.
I believe your issue is that you're exporting your Categories class twice, once connected, the other not.
If you remove export from export class Categories extends Component, does it work as expected?
When you're mapping the state in a component, you must access the desired variable through a reducer.
So instead of:
const mapStateToProps = state => ({
categories: state.categories
});
You must use:
const mapStateToProps = state => ({
categories: state.categories.categories
});
Your props don't have getCategories method, because you didn't pass it as a function to connect.
A better approach is to define only the action code in your actions file and then use mapDispatchToProps.
../../actions/categories.js
import axios from "axios";
export const getCategories = () => {
axios
.get("/api/notes/categories/")
.then(res => res.data);
})
.catch(err => console.log(err));
};
Categories.js
import { getCategories } from '../../actions/categories'
import { CATEGORIES_GET } from "./types";
const mapDispatchToProps = dispatch => {
return {
getCategories: () => dispatch(() => { type: CATEGORIES_GET, payload: getCategories() }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Categories);

Categories

Resources