如何通过反应调试给出此错误消息?弄清楚究竟是什么导致了它?我用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();
截图:
答案 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;在那里你可以找到类型无效的孩子。
答案 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; (对不起,我也不会说这种语法,但我可以看到发生了什么,基本上)