In Redux, reducer doesn't recognise the action object - javascript

I'm using Redux for a React project. For some reason, my reducer doesn't recognise the action type sent to it or even the action itself. I get this error TypeError: Cannot read property 'type' of undefined. And I know I'm using dispatch.
The api from the server works fine, I've tested through Postman.
But I don't understand what's happening with redux.
Please, can someone advise me?
Before stamping down my question, I've read many SO posts that looks similar but none has answered my question, hence why I'm asking it here.
Thanks.
Action:
import axios from 'axios';
export const GET_ALL_USERS = 'GET_ALL_USERS';
export const showAllUsers = () => dispatch => {
console.log('USERS ACTION');
return axios
.get('/api/users/all')
.then(res => {
console.log('GETTING ALL USERS ACTION', res);
return dispatch({
type: GET_ALL_USERS,
payload: res.data
});
})
.catch(err => console.log('Oops! Cannot get any users.'));
};
Reducer:
import { GET_ALL_USERS } from '../actions/usersActions';
const InitialState = {
users: null,
loading: false
};
export default function(state = InitialState, action) {
console.log('USERS REDUCER', action);
switch (action.type) {
case GET_ALL_USERS:
return {
...state,
users: action.payload
};
default:
return state;
}
}
React:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import './index.css';
import App from './containers/App';
import registerServiceWorker from './registerServiceWorker';
import rootReducer from './reducers';
let middleware = [thunk];
const store = createStore(
rootReducer,
{},
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
React component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { showAllUsers } from '../../actions/usersActions';
export class Admin extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.showAllUsers();
}
render() {
const { users } = this.props;
console.log(`All users in admin`, this.props.users);
return (
<div className="admin">
<h1 className="admin__title">Admin Board</h1>
{this.props.users.map(user => {
return (
<article>
<img src={user.avatar} alt={user.username} />
<p>{user.firstName}</p>
<p>{user.lastName}</p>
<p>{user.username}</p>
<p>{user.email}</p>
</article>
);
})}
</div>
);
}
}
const mapStateToProps = state => ({
users: state.users
});
export default connect(mapStateToProps, { showAllUsers })(Admin);

showAllUsers is async action. You need some middleware to dispatch async action.
Use redux-thunk or redux-saga and integrate it with store.
It will help to perform asynchronous dispatch
import thunkMiddleware from 'redux-thunk';
let middleWares = [thunkMiddleware];
const store = createStore(
rootReducer, applyMiddleware(...middleWares)
)
If you dispatch async action in synchronous way, the error TypeError: Cannot read property type of undefined will be thrown.

Related

Dispatched actions reach the action creator but not the receiver

My toolbar component dispatches an action abortGame(). I see in the console that it reaches the action creator (text "ONE!" displayed on the console) but never the reducer (text "TWO!" never displayed).
What is wrong?
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import App from './App';
import reducer from './store/reducers/reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
const app = (
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
ReactDOM.render(app, document.getElementById('root'));
reducer.js
import * as actionTypes from '../actions/actionTypes';
const initialState = {
score: 0
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ABORT_GAME:
console.log('TWO');
return {
...state,
score: 0,
};
default:
return state;
}
};
export default reducer;
actionTypes.js
export const ABORT_GAME = 'ABORT_GAME';
actions.js
import * as actionTypes from './actionTypes';
export const abortGame = () => {
console.log('ONE!');
return {
type: actionTypes.ABORT_GAME
};
};
Toolbar.js (component)
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { abortGame } from '../../store/actions/actions';
const toolbar = (props) => (
<div>
<div onClick={props.onAbortGame}><Link to="/">MyApp</Link></div>
</div>
);
const mapDispatchToProps = dispatch => {
return {
onAbortGame: () => dispatch(abortGame)
};
};
export default connect(null, mapDispatchToProps)(toolbar);
You have just missed the function brackets while dispatching the action
const mapDispatchToProps = dispatch => {
return {
onAbortGame: () => dispatch(abortGame)
};
};
Just change this line onAbortGame: () => dispatch(abortGame) to onAbortGame: () => dispatch(abortGame())
abortGame is a action creator which is basically a function. So you need to call the function inside dispatch
You just passing the function name in dispatch not calling it exactly.
Just convert the dispatch(abortGame())
sandbox working link

Unable to dispatch state values from redux store

I am new to react and redux and trying to dispatch states from store but unable to do so,
Please anyone help me to resolve the problem...
App.js
import React from "react";
import store from './reduxStore/store';
import { Provider } from 'react-redux';
import Sidebar from './Sidebar';
function App() {
return (
<div>
<Provider store={store}>
<Sidebar ></Sidebar>
</Provider>
</div>
);
}
export default (App);
This is my sidebar.js Component
import React, { useEffect } from 'react';
import { updatePageLinkActions } from "../../reduxStore/actions/updatePageLinkActions";
import { connect } from "react-redux";
const Sidebar = () =>{
useEffect(() => {
return updatePageLinkActions
}, [])
return (
<>
<ListItem button onClick={updatePageLinkActions}>
<ListItemIcon><Home></Home></ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button onClick={() => handleOpenPage("contact")}>
<ListItemIcon><AcUnit></AcUnit></ListItemIcon>
<ListItemText primary="Contact" />
</ListItem>
</>
)
}
const mapStateToProps = (state) => ({
updatePage: state.updatePage.pgLink
})
export default connect(mapStateToProps, { updatePageLinkActions })(SidebarItems);
Store Store.js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleWare = [thunk]
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleWare),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
)
)
export default store;
Root Reducer rootReducer.js
import { combineReducers } from 'redux';
import updatePageLinkReducer from './updatePageLinRreducer';
export default combineReducers({
updatePage: updatePageLinkReducer
});
Action updatePageLinkActions.js
import { UPDATE_PAGE_LINK } from "../actionTypes";
//import store from "../store";
export const updatePageLinkActions = dispatch => {
console.log("actions log ", dispatch.payload)
return dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
};
Reducer updatePageLinkReducer.js
import { UPDATE_PAGE_LINK } from '../actionTypes';
const initialState = {
pgLink: []
};
// eslint-disable-next-line import/no-anonymous-default-export
export default function(state = initialState, action) {
switch (action.type) {
case UPDATE_PAGE_LINK:
console.log("Action called ")
return { ...state, pgLink: action.payload }
default:
return state;
}
}
I am unable to dispatch values from store,
Please somebody help me...
If you are using functional components I strongly recommend getting rid of using connect.
To get state from Redux store, you can use useSelector hook, and to dispatch an action, you can use useDispatch hook.
import { useDispatch, useSelector } from 'react-redux'
...
const dispatch = useDispatch()
const updatePage = useSelector(state => state.updatePage.pgLink)
const updatePageLinkActions = () => {
dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
}
...
Your action creator is not defined properly.
It should be this:
export const updatePageLinkActions = () => dispatch => {
console.log("actions log ", dispatch.payload)
return dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
};

I can't fetch the data from reducer to component

I'm trying pass the data from reducer to component and receive as props.
But the data return UNDEFİNED, so I have tried console the data on reducer and action, but it's okey. There isn't any problem with the data coming from the API, but it always return to component undefined. Where is my fault?
Action
export default ProfileTab;
import axios from 'axios';
import { BASE, API_KEY } from '../config/env';
export const FETCHED_MOVIES = 'FETCHED_MOVIES';
export function fetchMovies() {
return (dispatch) => {
axios
.get(`${BASE}s=pokemon&apikey=${API_KEY}`)
.then((result) => result.data)
.then((data) =>
dispatch({
type: FETCHED_MOVIES,
payload: data.Search,
}),
);
};
}
Reducer
import { FETCHED_MOVIES } from '../actions/movies';
const initialState = {
fetching: false,
fetched: false,
movies: [],
error: {},
};
export default (state = initialState, action) => {
switch (action.type) {
case 'FETCHED_MOVIES':
return {
...state,
movies: action.payload,
};
default:
return state;
}
};
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { fetchMovies } from '../../actions/movies';
class Case extends Component {
static propTypes = {
movies: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchMovies();
}
onChangeHandler = (e) => {
this.setState({
input: e.target.value,
});
};
render() {
console.log(this.props.movies);
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
<h1>mert</h1>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
movies: state.movies,
};
};
const mapDispatchToProps = {
fetchMovies,
};
export default connect(mapStateToProps, mapDispatchToProps)(Case);
Do this in the connect statement:
export default connect(mapStateToProps,{fetchMovies})(Case);
And remove the mapDispatchToProps function from your code.
Dispatching props as an object is quite incorrect. Try this, and it should work.
That's because your mapDispatchToProps function should return an object and take dispatch as parameter. Each field in your returned object should contain a function that dispatches your action.
So try something like this:
const mapDispatchToProps = dispatch => {
return {
fetchMovies: () => dispatch(fetchMovies())
}
}
Although there's already an accepted answer, I'm not sure how correct it is, as it's completely valid to pass mapDispatchToProps the way you did with the latest react (16.13.1) and react-redux (7.2.1) versions (I'm not sure about earlier versions).
Now, assuming your question contains the whole code, there are two important things missing:
Creating the store:
import { createStore } from "redux";
const store = createStore(reducer);
and passing it to the Provider component:
<Provider store={store}>
If you go ahead and do as above, you'll see that this.props.fetchMovies emits the following error:
Actions must be plain objects. Use custom middleware for async actions.
To fix it, do as it says and add a middleware, e.g. thunk:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(thunk));
What follows is the full code. Note that I "split" fetchMovies into two functions: sync and async, for illustrating the difference usage between the two. I also modified your code (made is shorter, mostly) for this answer's readability. You can also see a live demo here:
File app.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMoviesSync, fetchMoviesAsyncMock } from "./api";
class App extends Component {
componentDidMount() {
this.props.fetchMoviesSync();
this.props.fetchMoviesAsyncMock();
}
render() {
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
{this.props.movies.join("\n")}
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({ movies: state.movies });
const mapDispatchToProps = {
fetchMoviesSync,
fetchMoviesAsyncMock
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
File api.js
export const FETCHED_MOVIES = "FETCHED_MOVIES";
export const fetchMoviesSync = () => ({
type: FETCHED_MOVIES,
payload: ["movie1", "movie2", "movie3", "movie4"]
});
export const fetchMoviesAsyncMock = () => (dispatch) => {
dispatch({
type: FETCHED_MOVIES,
payload: ["movie5", "movie6", "movie7", "movie8"]
});
};
File reducer.js
const initialState = {
movies: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case "FETCHED_MOVIES":
return {
...state,
movies: state.movies.concat(action.payload)
};
default:
return state;
}
};
File index.js
import React from "react";
import ReactDOM from "react-dom";
import Case from "./app";
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
let store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<Case />
</Provider>,
document.getElementById("container")
);
File index.html
<body>
<div id="container"></div>
</body>

Why state returned from my reducer is an empty object??(React-Redux)

I have a problem in my redux reducer, it does not return expected state after dispatching FETCH_BOOKS action, it returns an empty object instead of an object of state which is books that is fetched by AJAX request,
the reducer returns correct data when storing my state in array instead of object, this is so confusing, why does this happen??
These are my components
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import * as BooksAPI from './BooksAPI';
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware } from 'redux';
import { bookReducer } from './reducers/BookReducer';
import thunk from 'redux-thunk';
import {BrowserRouter as Router} from 'react-router-dom';
const middleware = [thunk];
const initialState = {};
const store = createStore(bookReducer, initialState, applyMiddleware(...middleware));
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import BookShelf from './components/BookShelf'
import AllShelves from './components/AllShelves'
import Header from './components/Header';
import SearchPage from './components/SearchPage';
import * as BooksAPI from './BooksAPI';
import { Route, withRouter } from 'react-router-dom';
import './App.css';
class App extends Component {
componentWillMount() {
this.props.fetchBooks();
}
render() {
console.log(this.props.books)
return (
<div className="App">
<Header />
<Route exact path="/" component={AllShelves} />
<Route path="/search" component={SearchPage} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
books: state.books
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchBooks: () => {
BooksAPI.getAll().then(books => dispatch({
type: 'FETCH_BOOKS',
books
}))
},
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App))
reducer that doesn't works
import { FETCH_BOOKS } from '../actions/Types.js';
import * as BooksAPI from '../BooksAPI'
const initialState = {
books: [],
query: ''
}
export const bookReducer = (state = initialState, action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return {
...state,
books: action.books,
}
default:
return state;
}
}
The reducer that work
export const bookReducer = (state = [], action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return action.books
default:
return state;
}
}
So why storing state in object doen't work and it works perfectly with array, i don't want to store my state in array, as books is not the only data i need to manage in my state!!!
I've checked all your codes and I think the problem possibly come from the redux store setup:
const initialState = {};
const store = createStore(bookReducer, initialState, applyMiddleware(...middleware));
I suggest removing the initialState:
const initialState = {}; // remove this line cuz we don't need it
const store = createStore(bookReducer, applyMiddleware(...middleware)); //fixed like this
In addition, I think you should fetch your books in the componentDidMount() lifecycle hook instead of componentWillMount(), like this:
componentDidMount() {
this.props.fetchBooks();
}
In the second example, you are fetching the value in the reducer as action.books, instead it should be action.payload because that's the key dispatched in action.
After any Action is dispatched you have to return a new state to the store so you have to return new state object for that you have to get the state now and change the books and return the new state so following code doing that
export const bookReducer = (state = initialState, action) => {
switch(action.type) {
case 'FETCH_BOOKS':
return {
...state,
books: action.books,
}
default:
return state;
}
}
but from the other reducer you return only the books array that coming from the action that is wrong way to do that

Can´t access props after using CombineReducer

My previous React-Redux implementation was working, but after I tried to implement the combineReducer function with seperated files, an error is thrown that I don´t really understand. Hope some of you can help me!
ERROR: Uncaught TypeError: this.props.todos.map is not a function
My Reference for that Code was the Async Example of the Redux-Doc´s. But I stated with another example and the change from each examples are not documented in the doc´s.
The first code I will show, is that I had (working):
MyStore
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import addItem from '../reducers/addItem'
export default function configureStore(preloadedState) {
const store = createStore(
addItem,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
return store
}
My Reducer
export default (state = ['Test'], action) => {
switch (action.type){
case 'ADD_ITEM':
//return action.item
return [
...state,
{
id: action.id,
text: action.item
}
]
default:
return state
}
}
Actions
export default function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
id: nextTodoId++,
item
}
}
And the subComponent where the input is finally rendered
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
export default class TodoList extends Component {
render() {
const posts = this.props
const isEmpty = posts.length === 0
return (
<div>
<h3>Meine Aufgaben</h3>
<ul>
{isEmpty
? <h3>Sie haben noch keinen Todo´s angelegt</h3>
: <h3>Ihre Ergebnisse</h3>
}
{this.props.todos.map((todo, i) => <li key={i}>{todo.text} </li>)}
</ul>
</div>
)
}
}
const mapStateToProp = state => ({todos: state})
export default connect (mapStateToProp)(TodoList)
What I have change:
First, I created another Reducers File, called Index where I imported the addItem Reducer and exported the rootReducer:
import {combineReducers} from 'redux'
import addItem from './addItem'
import getItem from './getItem'
const rootReducer = combineReducers({
addItem,
getItem
})
export default rootReducer
After that, I changed the Store to import the rootReducer and put it´s reference in the Store (just the changes to configureStore):
import rootReducer from '../reducers/index'
const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
I don´t know if that Information is also required, but here is my Container Component:
import React, { Component, PropTypes } from 'react'
import AddTodo from '../components/AddTodo'
import TodoList from '../components/TodoList'
import { connect } from 'react-redux'
import addItem from '../actions/addItem'
import getItems from '../actions/getItems'
class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {text: ''}
}
handleClick(e){
console.log(e);
const {dispatch} = this.props
dispatch(addItem(e));
}
componentDidMount(){
console.log("COMPONENT MOUNT");
const {dispatch} = this.props
// dispatch(getItems())
}
componentWillReceiveProps(nextProps) {
console.log("GETTT IT");
console.log(nextProps)
}
render() {
return (
<div>
< h1 > Hallo </h1>
<AddTodo handleAddItem={this.handleClick}/>
<TodoList/>
</div>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state){
return {
AddTodo
}
}
export default connect (mapStateToProps)(App)
I hope this issue is not to basic and someone can help me. Thanks in advance!
If you inspect your redux state you will see that the following code sets up 2 more keys in the state (addItem and getItem):
const rootReducer = combineReducers({
addItem,
getItem
})
So, now to connect todos you need to one of the 2 new keys. If todos is not defined on those, then you need to add the reducer of todos to the combineReducers call.
So this needs to map to a valid location in state:
const mapStateToProp = state => ({todos: state})

Categories