React - 元素类型无效 - 如何调试此错误?

时间:2016-11-17 20:26:51

标签: reactjs

如何通过反应调试给出此错误消息?弄清楚究竟是什么导致了它?我用Google搜索了错误,但似乎是由不同的事情造成的。

invariant.js:38 Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

由此代码给出:

// @flow
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore , combineReducers} from 'redux'
import deepFreeze from 'deepfreeze'
import expect from 'expect'
var _ = require('lodash')

type State$Todo = {
  text:string;
  completed:boolean;
  id:number;
};

class Todo {
  static make(t:string,id:number):State$Todo{
    return {text:t,id:id,completed:false}
  }
  static toggle(t:State$Todo):State$Todo {
    return {...t, completed:!t.completed};
  }
};

type Action$SetVisibilityFilter = {
     type:'SET_VISIBILITY_FILTER',
     filter:State$VisibilityFilter
};

type Action$ADD_TODO = {
  type:'ADD_TODO',
  text:string,
  id:number
};

type Action$TOGGLE_TODO = { type:'TOGGLE_TODO', id:number }

type Action$Todo = Action$ADD_TODO | Action$TOGGLE_TODO

type Action$App = Action$Todo | Action$SetVisibilityFilter

type State$TodoList = State$Todo[];

type State$VisibilityFilter = 'SHOW_ACTIVE' | 'SHOW_ALL' | 'SHOW_COMPLETED'

type State$App = {
  todos:State$TodoList,
  visibilityFilter:State$VisibilityFilter
}

const todosReducer = (state: State$TodoList=[], action: Action$App) :State$TodoList=>{
      switch (action.type){
        case 'ADD_TODO' : return [ ... state, Todo.make(action.text, action.id)];
        case 'TOGGLE_TODO':
          const id=action.id;
          return  _.map(state, (td) => (td.id==id) ? Todo.toggle(td) : td );
        default : return state;
      }
};

const visibilityFilterReducer = (state:State$VisibilityFilter = 'SHOW_ALL', action:Action$App) : State$VisibilityFilter =>  {
  switch(action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default : return state;
  }
}

const todoApp = (state : State$App = {todos:[],visibilityFilter:'SHOW_ALL'}, action: Action$App) : State$App => {
  return { todos: todosReducer(state.todos, action), visibilityFilter: visibilityFilterReducer(state.visibilityFilter,action) };
}

//const todoApp =combineReducers({todos:todosReducer, visibilityFilter:visibilityFilterReducer})
const store  = createStore (todoApp)



type FilterLinkProps={
  filter:State$VisibilityFilter,
  currentFilter:State$VisibilityFilter,
  children:React$Element<*>
};

const FilterLink = ({
  filter,
  currentFilter,
  children
}:FilterLinkProps) => {
  if(filter===currentFilter) {
    return <span>{children}</span>
  }
  return (
    <a href='#'
      onClick={e => {
        e.preventDefault();
        store.dispatch(({
          type: 'SET_VISIBILITY_FILTER',
          filter
        }:Action$SetVisibilityFilter));
      }}
    >
    {children}
    </a>
  );
};

const getVisibleTodos = (
  todos:State$TodoList,
  filter:State$VisibilityFilter
) : State$TodoList => {
  switch (filter) {
    case ('SHOW_ALL' :State$VisibilityFilter):
      return todos;
    case ('SHOW_COMPLETED':State$VisibilityFilter):
      return todos.filter(
        t => t.completed
      );
    case ('SHOW_ACTIVE':State$VisibilityFilter):
      return todos.filter(
        t => !t.completed
      );
    default:
      throw "undefined action"
  }
}

let nextTodoId = 0;
const TodoReactElement=(props:{onClick:Function,completed:boolean,text:string}) =>(
            <li onClick={props.onClick}
                style ={{ textDecoration: props.completed ? 'line-through' : 'none'}} >
                {props.text}
            </li>
);

type TodoListReactComponentProps ={todos:State$TodoList,onTodoClick:Function}

const TodoList =(props:TodoListReactComponentProps) =>(
  <ul>
    {props.todos.map( todo=>
      <TodoReactElement
        key ={todo.id}
        completed={todo.completed}
        onClick={()=> props.onTodoClick(todo.id)}
        text= {todo.text} >
      </TodoReactElement>)}
  </ul>
)


class TodoApp extends React.Component {
  render() {

    const todos : State$TodoList= this.props.todos;
    const visibilityFilter :State$VisibilityFilter=
          this.props.visibilityFilter;
    const visibleTodos :State$TodoList = getVisibleTodos(
      todos, visibilityFilter );

    return (
      <div>
      <input ref ={ node => {this.input=node;} } />
        <button onClick={() => {
          store.dispatch(({
            type: 'ADD_TODO',
            text: 'Test'+this.input.value,
            id: nextTodoId++
          } : Action$ADD_TODO));
          this.input.value='';
        }}>
          Add Todo
        </button>
        <TodoList todos={visibleTodos}
                  onTodoClick={id=> store.dispatch(({type:'TOGGLE_TODO',id}:Action$TOGGLE_TODO))}>
        </TodoList>
        <p>
          Show:
          {' '}
          <FilterLink
            filter='SHOW_ALL'
            currentFilter={visibilityFilter}
          >
          All
          </FilterLink>
          {' '}
          <FilterLink
            filter='SHOW_ACTIVE'
            currentFilter={visibilityFilter}
          >
          Active
          </FilterLink>
          {' '}
          <FilterLink
            filter='SHOW_COMPLETED'
            currentFilter={visibilityFilter}
          >
          Completed
          </FilterLink>
        </p>

      </div>
    );
  }
}
const root   = document.getElementById('root')
const render = () => {
  ReactDOM.render(
    <TodoApp {...store.getState()} />,root
  );
};

store.subscribe(render)
render();

截图:

enter image description here

3 个答案:

答案 0 :(得分:3)

不幸的是我只会说Javascript,而不是Typescript(或其他任何东西),但错误本身非常清楚:你试图渲染React不期望的东西(一个对象)(因为它需要字符串/函数)。

我看到两种可能性:

1)invariant.js中存在错误;由于错误来自那里,这可能是问题,但更可能......

2)您的一个渲染方法包括(在其return值中)一个对象

不幸的是,正如您所发现的,React堆栈跟踪并不总是特别有用。就个人而言,我建议您只评论类的render方法,一次一个,从最外层的方法开始(在我的情况下,我认为是FilterLink),并使用简单的return <div/>暂时替换它们。

然后尝试再次产生错误。如果你仍然得到它,恢复render方法并对组件链中的下一个类做同样的事情。如果没有,您就找到了有问题的render。如果您无法通过查看问题立即查看问题,请尝试记录其中涉及的每个变量(或者,如果您使用Lodash / Underscore,_.isObject(thatVariable)),直到找到问题为止。

答案 1 :(得分:1)

我认为错误信息很简单,但在哪个组件中?这就是问题。&#xA;实际上,在callstack中,有一些点有一些父组件。

&#xA;&#xA;

instantiateReactComponent(如 instantiateReactComponent)上添加一个断点instantiateReactComponent.js:74)并重试。&#xA;单击 mountComponent ... reactConciler.js ... 将引导您调用 internalInstance.mountComponent 。在internalInstance中,你可以在 _currentElement.type 中找到一些有意义的元素类型。&#xA;在那里你可以找到类型无效的孩子。

&#xA;&#xA;

&#xA;

答案 2 :(得分:0)

您已在此处指明了FilterLink&#34; children&#34; prop只接受React元素:

type FilterLinkProps={
  // ... snipped ...
  children:React$Element<*>
};

...但是您传递的是非React元素(对象,字符串等)。您需要将道具类型更改为&#34; React.PropTypes.node&#34;而不是&#34; React.PropTypes.element&#34; (对不起,我也不会说这种语法,但我可以看到发生了什么,基本上)