reduxjs / react-redux

Official React bindings for Redux
https://react-redux.js.org
MIT License
23.37k stars 3.37k forks source link

must return an object. Instead received [object Object]. #334

Closed EasonWang01 closed 8 years ago

EasonWang01 commented 8 years ago

hi,I'm new to this

the error message is browser.js:40 Uncaught Invariant Violation:mapStateToPropsmust return an object. Instead received [object Object]. 1.

        case 'TOGGLE_TODO':

        return  state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                return {...state,completed:true}
            });

(from egghead ) this will throw the error,but I don't know why.

But if I not using return before state.todos.map it is fine

2.

        case 'TOGGLE_TODO':

            state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                //return state.completed=true}
                return {...state,completed:true}
            });

if I change to this it will not change the store

3. only

        case 'TOGGLE_TODO':

            state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                //return state.completed=true}
                return state.completed=true
            });

and

        case 'TOGGLE_TODO':

            state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                //return state.completed=true}
                return Object.assign(state,{completed:true})
            });

can successfully update the store and no error message,but this will also change the prev state,

how can I change this code? Thanks for reply.

gaearon commented 8 years ago

If you found the solution please share it in a comment so other people who find this issue know how you solved it. Thanks!

EasonWang01 commented 8 years ago

I was using below works good ,but still don't know why others give error when it return [object Object]but still says must return an object

case 'TOGGLE_TODO':

      return Object.assign({},state,{todos:state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                return {...state,completed:!state.completed}

            }) }
      )
gaearon commented 8 years ago

Why are you posting the reducer code? The error says mapStateToProps returned something that isn’t a plain object. Not the reducer.

My guess is your mapStateToProps returns something like state.todos which will not work. The point of that function is to provide props to component, so it has to return an object with those props. Rather than pass state.todos you would probably want { todos: state.todos }.

If this is the case, we need to make a better error message that detects arrays. Contributions are welcome.

alexandradeas commented 8 years ago

The problem here is that the error message isn't clear enough, an object is being passed in but it isn't a plain object until EasonWang01 uses Object.assign(). checkStateShape calls _.isPlainObject which requires Object.prototype == null or to have been created with Object.constructor. The error message could probably be updated to read %sToProps must return a plain object. Instead received %s

The wording for computeMergedProps could be updated as well to reflect this.

EasonWang01 commented 8 years ago

the mapStateToProp is look like this

import React, { Component } from 'react'
import TodoInput from './TodoInput.js'
import TodoList from './TodoList.js'
import {connect} from 'react-redux'

class App extends Component {

  render() {
    return (
      <div>
        <h1>Todo list</h1>
        <TodoInput />
        <TodoList  todos={this.props}/>

      </div>
    )
  }

}
function  mapStateToProp(state){

    return state
}

export default connect(mapStateToProp)(App)

I guess it might be that I'm not using combineReducer so I need to return the full state to store,not sure~ (◕ᴥ◕) ʕ·ᴥ·ʔ

gaearon commented 8 years ago

Can you provide a full project reproducing the problem on GitHub? These pieces don't quite add up in my mind. For example it is strange you are passing the whole props object as todos prop down.

EasonWang01 commented 8 years ago

Hi, https://github.com/EasonWang01/Redux-tutorial/tree/link it's the origin version.

in Redux folder=> reducer.js

change to below and click initial for demo

let getId = 1 ;

export default function reducer(state,action){
    switch(action.type){
        case 'ADD_TODO':

            return( Object.assign({},state,{
                todos:[{
                  text:action.text,
                  completed:false,
                  id:getId++

                },...state.todos]
            })
            )
        case 'TOGGLE_TODO':

  return  state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                return {...state,completed:true}
            });

        case 'SET_VISBILITY_FILTER':

        return Object.assign({},state,{visbility:action.filter}) 

        default:
            return state;

    }

}

then the error appear,I think it just some miss typed by me, not a big problem need to solve ( ´ ▽ ` )b

EasonWang01 commented 8 years ago

But one thing I found is when some error occur in reducer.js the console.error will always point to some component file rather than reducer.js file.

gaearon commented 8 years ago

Normally you return an object like { todos: Array<Todo> }:

        case 'ADD_TODO':

            return( Object.assign({},state,{
                todos:[{
                  text:action.text,
                  completed:false,
                  id:getId++

                },...state.todos]
            })
            )

However when handling TOGGLE_TODO, you return just an Array<Todo>:

        case 'TOGGLE_TODO':

  return  state.todos.map(function(state){
                if(state.id!==action.id){
                return  state
                };

                return {...state,completed:true}
            });

This is missing Object.assign({}, state, { todos: ... }).

Then this state gets returned from mapState() which is where the error fires. Fixing the reducer to return the object would fix the problem. I also suggest you to use reducer composition as Egghead series shows, rather than put all the code into a single big reducer.

Hope it helps!

EasonWang01 commented 8 years ago

thanks!