react-redux 4.0.6 and Meteor

Created on 15 Jan 2016  路  11Comments  路  Source: reduxjs/react-redux

I am following the Get Started Videos for Redux and I have this error Uncaught TypeError: Cannot read property 'displayName' of undefined with this config:

    "meteor": "1.3.beta4",
    "redux": "3.0.5",
    "react-redux": "4.0.6",
    "redux-simple-router": "1.0.2"

Here is my code:

import { createStore } from 'redux'
import { Component } from 'react'
import { combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'

if (Meteor.isClient) {
  Meteor.startup(function () {

    const todoReducer = (state, action) => {
      switch (action.type){
        case 'ADD_TODO':
          return {
            id: action.id,
            text: action.text,
            completed: false
          };

        case 'TOGGLE_TODO':
          if (state.id != action.id) return state
          else{
            return {
              ...state,
              completed: !state.completed
            };
          }


        default:
          return state;
      }
    };

    const todosReducer = (state = [], action) => {
      switch (action.type){
        case 'ADD_TODO':
          return [
            ...state,
            todoReducer(undefined, action) // no state for a new todo
          ];

        case 'TOGGLE_TODO':
          return state.map(t => todoReducer(t, action));

        default:
          return state;
      }
    };

    const visibilityFilterReducer = (state = 'SHOW_ALL', action) => {
      switch(action.type){

        case 'SET_VISIBILITY_FILTER':
          return action.filter;

        default:
          return state;
      }
    };

    const rootReducer = combineReducers({
      todosReducer,
      visibilityFilterReducer
    });


    const Link = ({
      active,
      children,
      onClick
    }) => {
      if(active){
        return (
          <span>{children}</span>
        );
      }

      return (
        <a href="#"
           onClick={(e)=>{
            e.preventDefault();
            onClick(active);
           }}
        >{children}</a>
      );
    };

    class FilterLink extends Component{

      componentDidMount(){
        const {store} = this.context;
        this.unsubscribe = store.subscribe(()=>
          this.forceUpdate()
        )
      }

      componentWillUnmount(){
        this.unsubscribe();
      }

      render(){
        const {store} = this.context;
        const props = this.props;
        const state = store.getState();

        return(
          <Link
            active={props.filter === state.visibilityFilterReducer}
            onClick={()=>
              store.dispatch({
                type:'SET_VISIBILITY_FILTER',
                filter:props.filter
              })
            }
          >
            {props.children}
          </Link>
        );
      }
    }
    FilterLink.contextTypes = {
      store: React.PropTypes.object
    };

    const getVisibleTodos = (todos, filter)=>{
      switch(filter){
        case 'SHOW_ALL':
          return todos;
        case 'SHOW_ACTIVE':
          return todos.filter(t => !t.completed);
        case 'SHOW_COMPLETED':
          return todos.filter(t => t.completed);
      }
    }

    const Todo = ({
      onClick,
      completed,
      text
    }) => (
      <li onClick={onClick}
          style = {{
            textDecoration:
              completed ?
                'line-through':
                'none'
          }}>
        {text}
      </li>
    );

    const mapStateToProps = (state) => {
      return {
        todos: getVisibleTodos(
          state.todosReducer,
          state.visibilityFilterReducer
        )
      }
    }

    const mapDispatchToProps = (dispatch) => {
      return {
        todos: id =>
          dispatch({
            type:'TOGGLE_TODO',
            id
          })
      }
    };

    const VisibleTodoList = connect(
      mapStateToProps,
      mapDispatchToProps
    )(TodoList);

    const TodoList = ({
      todos,
      onTodoClick
      }) => (
      <ul>
        {todos.map(todo =>
          <Todo
            key={todo.id}
            {...todo}
            onClick={() => onTodoClick(todo.id)}
          ></Todo>
        )}
      </ul>
    );


    const AddTodo = (props, {store}) => {
      let input;

      return(
        <div>
          <input type="text" ref={
              node =>{
                input = node;
              }
            }/>
          <button onClick={() => {
            store.dispatch({
              type:'ADD_TODO',
              id: nextTodoId++,
              text: input.value
            });
            input.value = '';
          }}>Add todo</button>
        </div>
      )
    };
    AddTodo.contextTypes = {
      store: React.PropTypes.object
    };

    const Footer = () => (
      <p>
        Show:
        {' '}
        <FilterLink filter='SHOW_ALL'>All</FilterLink>
        {' '}
        <FilterLink filter='SHOW_ACTIVE'>Active</FilterLink>
        {' '}
        <FilterLink filter='SHOW_COMPLETED'>Completed</FilterLink>
      </p>
    );

    let nextTodoId = 0;
    const TodoApp = () => (
      <div>
        <AddTodo/>
        <VisibleTodoList/>
        <Footer/>
      </div>
    );

    ReactDOM.render(
      <Provider store={createStore(rootReducer)}>
        <TodoApp/>
      </Provider>,
      document.getElementById('render-target')
    );
  });
}

I will appreciate any help on this. I don't find any FAQ or troubleshooting section on this.

Most helpful comment

Figure it out. I forgot to declare TodoList BEFORE const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList);

All 11 comments

Figure it out. I forgot to declare TodoList BEFORE const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList);

I did the exact same thing, and was about to give after over an hour of fighting with it and having no idea what was causing the issue...then I came across your post. Thank you, this was really helpful. 9 times out of 10 it's something small :)

Whereever you are whoever you are. I will find you. And thank you!

Quite an old issue, and a frustrating one when it bites.

Any hope of a better error message in this situation?

This is really an issue with ES6 syntax and behavior. By declaring a variable using const or let, it does not exist until the line it is declared on (technically known as the "temporal dead zone"). So, trying to use a variable before that declaration line is going to cause things to break.

@markerikson I'm not sure what that means. Are you saying "because of the temporal dead zone, we can't do better with an error message for this issue"?

The point is that there's nothing specific to Redux about this issue. It's effectively as if you were manually calling connect(mapState)(undefined).

About the only thing we could do would be add some kind of assertion that WrappedComponent is an actual function, and throw an error if it isn't.

If it's like calling connect(mapState)(undefined) then it'd be great for the message to say so.

uncaught TypeError: Cannot read property 'displayName' of undefined

doesn't really capture that. It doesn't even tell you which file it's coming from.

If you'd like to file a PR that adds some validation for the WrappedComponent argument in a development build, we could definitely take a look at adding that. Would mostly need to make sure that the PR is made against the next branch.

Yeah - I had a look at doing something, but it appears way non-trivial, due to the lack of obvious context at the point that this error message is issued.

Any clues about how you'd like it done?

Actually, I have good news for you. Looking at the next branch, it seems that @jimbolla has already implemented such a check, here: connectAdvanced.js#L67-L71

Was this page helpful?
0 / 5 - 0 ratings