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})
Related
So I am learning how to use redux and redux persist and currently I stuck at a problem right now. Whenever I try to map my store's content, it throws an error saying TypeError: tasks.map is not a function and I don't really know why the problem is occuring. I've googled around and tried debugging it, also tried to re-write the code a couple of time but it was all to no avail. Here's my entire code:
rootReducer.js
import React from 'react'
let nextToDo = 0;
const task=[
{
}
]
const reducer =(state=task, action)=>
{
switch(action.type)
{
case 'add':
return[
...state,
{
name: 'new',
id: nextToDo+=1
}
]
default:
return state;
}
}
export default reducer
store.js
import reducer from './rootReducer'
import {applyMiddleware,createStore} from 'redux'
import logger from 'redux-logger'
import {persistStore, persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
const persistConfig ={
key: 'root',
storage
}
export const persistedReducer = persistReducer(persistConfig, reducer)
export const store = createStore(persistedReducer, applyMiddleware(logger));
export const persistor = persistStore(store);
export default {store, persistor}
Index.js:
import React, {Component} from 'react'
import {createStore} from 'redux'
import reducer from './rootReducer'
import 'bootstrap/dist/css/bootstrap.min.css'
import {Button} from 'react-bootstrap'
import {store} from './store'
import {connect} from 'react-redux'
class Index extends Component {
render(){
const {tasks} = this.props
const add=()=>
{
store.dispatch(
{
type: 'add',
}
)
}
store.subscribe(()=>console.log('your store is now', store.getState()))
return (
<div>
{tasks.map(task=><div>{task.name}</div>)}
<Button onClick={()=>add()}></Button>
</div>
)
}
}
const mapStateToProps=(state)=>
{
return{
tasks: state
}
}
export default connect(mapStateToProps)(Index)
Try to declare state this way:
state = {
tasks: [{}]
}
Then, on Index.js, you can pass it to props by doing this:
const mapStateToProps=(state)=>
{
return {
tasks: state.tasks
}
}
And use it inside the Component with props.tasks
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>
Im using Redux with React Native to manage state. I believe that I've successfully set up the store and Provider. I can use store.getState() and store.dispatch(action()) from any component successfully, however, the react-redux connect function is not allowing me to access the store from child components. Can you find anything wrong with my code below?
Login.js - This child component I'm testing won't access redux store with react-redux connect.
import React, {Component} from 'react';
import actions from '../../redux/actions';
import {connect} from 'react-redux';
const mapStateToProps = state => {
// To test if this function fires, which it is not
console.log('login state mapping through redux');
return {
state: state,
};
};
const dispatchToProps = dispatch => {
return {
userRecieved: (user) => dispatch(actions.userRecieved(user)),
};
};
export class Login extends Component {
constructor(){
super();
this.state = {
credentials: {
email: '',
password: '',
},
};
}
componentDidMount(){
// This will show whether redux is connected
console.log(this.props.state);
this.props.userRecieved('TEST USER');
}
render() {
return ( <Text>{this.props.state}</Text> );
}
}
export default connect(mapStateToProps, dispatchToProps)(Login);
App.js
import React, {Component} from 'react';
import YEET from './src/YEET.js';
import store from './src/redux/stores/index';
import {Provider} from 'react-redux';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<YEET />
</Provider>
);
}
}
My Redux Files:
store.js
import { combineReducers, createStore} from 'redux';
import accountReducer from '../reducers/accountReducer';
import postReducer from '../reducers/postReducer';
const initialState = {};
const reducers = combineReducers({
account: accountReducer,
post: postReducer,
});
const store = createStore(reducers, initialState);
export default store;
actions.js
import constants from '../constants';
var userRecieved = user => ({
type: constants.USER_RECIEVED,
data: user,
});
export default {
userRecieved,
};
accountReducer.js
import constants from '../constants';
var initialState = {
user: {
photos: [],
},
};
export default (state = initialState, action ) => {
let newState = Object.assign({}, state);
switch (action.type) {
case constants.USER_RECIEVED:
const user = {
id: action.data.uid,
// photos: action.data,
};
console.log(action);
newState.user = user;
return newState;
default:
return state;
}
};
From what I see, the only reason could be that you're importing the unconnected component.
When you import the Login component, make sure that you import the default export instead of the named export.
So, wherever you import the Login component, do it like this:
import Login from 'your-login-component-location/Login'
instead of
import { Login } from 'your-login-component-location/Login'
The second one is a named export, which will return the Login class directly.
The first one is the default export, which will return the connected component.
i'm another guy with the same question; Sorry, but I could not see yet my error.I'm suspecting is something about mutation, but I tried (almost) everything, getting mad. My component is not updating when I add stuff to my store, it seems is rendering just for once (first time)
I was trying with lifecycles but nothing
ACTION
export default async function getJoke() {
let jk = await axios.get('https://jokeapi.p.rapidapi.com/category/any' ,{ headers :{
'x-rapidapi-host' : 'jokeapi.p.rapidapi.com',
'x-rapidapi-key' : "***********************" }} );
let joke;
if(jk.data.type === 'single') {
joke = jk.data.joke
} else {
joke = { setup : jk.data.setup,
delivery : jk.data.delivery}
}
let getjoke = {
type : 'GETJOKE',
joke
}
store.dispatch(getjoke) ;
}
REDUCER
let initialState = { jokes : [ ] };
function joke (state = initialState, action) {
switch (action.type) {
case 'GETJOKE' : return {...state,
jokes :[...state.jokes, {jokes: action.joke}]
} ;
default : return state;
}
}
export default joke;
REDUCER-INDEX
import { combineReducers } from 'redux';
import joke from './joke'
export default combineReducers ({
joke
})
STORE
import { createStore } from 'redux';
import Combined from './reducer';
export default createStore(Combined);
COMPONENT
import React from 'react';
import BotJoke from './btnJoke';
import {connect} from 'react-redux';
import store from '../store'
class joke extends React.Component {
constructor(props) {
super(props);
this.state = {
jokes : props.jokes
}
console.log('MENDA',this.state.jokes);
}
componentDidMount() {
console.log('willdisssd receive props');
/* this.setState({jokes : [...this.state.jokes, nextProps]})
*/
}
componentWillMount(){
console.log('will mount',this.state.jokes);
console.log('Entra');
console.log('NEXT PROPS');
}
render() {
return (
<div>
<BotJoke/>
{ this.state.jokes !== undefined ? <h1>Hay bromas</h1>: <h1>NO HAY BROMAS</h1> }
</div>
)
} }
function mapStateToProps (state) {
console.log('STorE', store.getState());
return {
jokes : state.jokes
}
}
let connection = connect(mapStateToProps)(joke);
export default connection;
What you are doing is an anti pattern, using the props to setState though you can just use props directly instead.
Use this.props.jokes everywhere in your component, it should work. Let me know.
EDIT:
Assuming you want the jokes to be an array:
REDUCER
let initialState = { jokes : [ ] };
function joke (state = initialState, action) {
switch (action.type) {
case 'GETJOKE' : return {...state,
jokes :[...state.jokes, action.joke]
} ;
default : return state;
}
}
export default joke;
REDUCER-INDEX
import { combineReducers } from 'redux';
import joke from './joke'
export default combineReducers ({
joke
})
STORE
import { createStore } from 'redux';
import Combined from './reducer';
export default createStore(Combined);
COMPONENT
import React from 'react';
import BotJoke from './btnJoke';
import {connect} from 'react-redux';
import store from '../store'
const joke = (props) => {
const length = props.jokes.length
return (
<div>
<BotJoke/>
{ length !== 0 ? <h1>{JSON.stringify(props.jokes[length-1])}</h1>: <h1>NO HAY BROMAS</h1> }
</div>
)
}
function mapStateToProps (state) {
console.log('STorE', store.getState());
return {
jokes : state.jokes
}
}
let connection = connect(mapStateToProps)(joke);
export default connection;
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.