React - Element type is invalid - how to debug this error? - reactjs

How can this error message given by react debugged ? To figure out what is really causing it ? I googled the error but it seems to be caused by different things.
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.
given by this code:
// #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();
screenshot:

Unfortunately I only speak Javascript, not Typescript (or whatever that was), but the error itself is pretty clear: you tried to render something (an object) that React wasn't expecting (because it expects strings/functions).
I see two possibilities:
1) there is a bug in invariant.js; since the error came from there this could be the problem, but more likely ...
2) one of your render methods includes (in its return value) an object
Unfortunately, as you've discovered, React stack traces are not always particularly helpful. Personally I would recommend just commenting out the render methods of your classes, one at a time, starting with the outermost one (which I think is FilterLink in your case), and replace them temporarily with a simple return <div/>.
Then try to produce the error again. If you still get it, restore the render method and go do the same thing to the next class up the component chain. If not, you've found your problematic render. If you can't immediately see the problem by looking at it, try logging every variable involved in it (or, if you use Lodash/Underscore, _.isObject(thatVariable)) until you find the problem.

I think the error message is quite straight forward, but in what component? That's the question.
Actually, in the callstack, there are some points has some parent components.
Add a break point at such as instantiateReactComponent (instantiateReactComponent.js:74) and retry.
Clicking mountComponent ...reactConciler.js... will lead you to calling internalInstance.mountComponent. and in the internalInstance, you can find some meaningful element type in _currentElement.type.
There you could find the child with invalid type.

You've specified here that FilterLink's "children" prop only accepts React elements:
type FilterLinkProps={
// ... snipped ...
children:React$Element<*>
};
...but you are passing non-React elements (objects, strings, etc). You need change prop type to "React.PropTypes.node" instead of "React.PropTypes.element" (sorry I don't speak that syntax either, but I can see what's going on, basically)

If someone could not relate his/her issue with other answers, this might be because of non-imported components. I had instantiated component and saved as element in a variable and used it in render function of another component.
//mainComp.tsx
const icon = <SomeIcon/>
return <div>{icon}</div>
* Did not get any error that SomeIcon is not imported. while trying to render this element type will be undefined.

Related

State changed in context provider not saved

So I'm trying to centralize some alert-related logic in my app in a single .tsx file, that needs to be available in many components (specfically, an "add alert" fuction that will be called from many components). To this end I am trying to use react context to make the alert logic available, with the state (an array of active alerts) stored in App.tsx.
Alerts.tsx
export interface AlertContext {
alerts: Array<AppAlert>,
addAlert: (msg: React.ReactNode, style: string, callback?: (id: string) => {}) => void,
clearAlert: (id: string) => void
}
[...]
export function AlertsProvider(props: AlertsProps) {
function clearAlert(id: string){
let timeout = props.currentAlerts.find(t => t.id === id)?.timeout;
if(timeout){
clearTimeout(timeout);
}
let newCurrent = props.currentAlerts.filter(t => t.id != id);
props.setCurrentAlerts(newCurrent);
}
function addAlert(msg: JSX.Element, style: string, callback: (id: string) => {}) {
console.log("add alert triggered");
let id = uuidv4();
let newTimeout = setTimeout(clearAlert, timeoutMilliseconds, id);
let newAlert = {
id: id,
msg: msg,
style: style,
callback: callback,
timeout: newTimeout
} as AppAlert;
let test = [...props.currentAlerts, newAlert];
console.log(test);
props.setCurrentAlerts(test);
console.log("current alerts", props.currentAlerts);
}
let test = {
alerts: props.currentAlerts,
addAlert: addAlert,
clearAlert: clearAlert
} as AlertContext;
return (<AlertsContext.Provider value={test}>
{ props.children }
</AlertsContext.Provider>);
}
App.tsx
function App(props: AppProps){
[...]
const [currentAlerts, setCurrentAlerts] = useState<Array<AppAlert>>([]);
[...]
const alertsContext = useContext(AlertsContext);
console.log("render app", alertsContext.alerts);
return (
<AlertsProvider currentAlerts={currentAlerts} setCurrentAlerts={setCurrentAlerts}>
<div className={ "app-container " + (error !== undefined ? "err" : "") } >
{ selectedMode === "Current" &&
<CurrentItems {...currentItemsProps} />
}
{ selectedMode === "History" &&
<History {...historyProps } />
}
{ selectedMode === "Configure" &&
<Configure {...globalProps} />
}
</div>
<div className="footer-container">
{
alertsContext.alerts.map(a => (
<Alert variant={a.style} dismissible transition={false} onClose={a.callback}>
{a.msg}
</Alert>
))
}
{/*<Alert variant="danger" dismissible transition={false}
show={ error !== undefined }
onClose={ dismissErrorAlert }>
<span>{ error?.msg }</span>
</Alert>*/}
</div>
</AlertsProvider>
);
}
export default App;
I'm calling alertsContext.addAlert in only one place in CurrentItems.tsx so far. I've also added in some console statements for easier debugging. The output in the console is as follows:
render app Array [] App.tsx:116
XHRGEThttp://localhost:49153/currentitems?view=Error [HTTP/1.1 500 Internal Server Error 1ms]
Error 500 fetching current items for view Error: Internal Server Error CurrentItems.tsx:94
add alert triggered Alerts.tsx:42
Array [ {…}, {…} ] Alerts.tsx:53
current alerts Array [ {…} ] Alerts.tsx:55
render app Array []
So I can see that by the end of the addAlert function the currentAlerts property appears to have been updated, but then subsequent console statement in the App.tsx shows it as empty. I'm relatively new to React, so I'm probably having some misunderstanding of how state is meant to be used / function, but I've been poking at this on and off for most of a day with no success, so I'm hoping someone can set me straight.
const alertsContext = useContext(AlertsContext);
This line in App is going to look for a provider higher up the component tree. There's a provider inside of App, but that doesn't matter. Since there's no provider higher in the component tree, App is getting the default value, which never changes.
You will either need to invert the order of your components, so the provider is higher than the component that's trying to map over the value, or since the state variable is already in App you could just use that directly and delete the call to useContext:
function App(props: AppProps){
[...]
const [currentAlerts, setCurrentAlerts] = useState<Array<AppAlert>>([]);
[...]
// Delete this line
// const alertsContext = useContext(AlertsContext);
console.log("render app", currentAlerts);
[...]
{
currentAlerts.map(a => (
<Alert variant={a.style} dismissible transition={false} onClose={a.callback}>
{a.msg}
</Alert>
))
}
}

Observe (get sized) control (listen to events) over a nested component in the react and typescript application via the forwardRef function

I have a functional component called MyDivBlock
const MyDivBlock: FC<BoxProps> = ({ }) => {
{getting data...}
return (
<>
<div className='divBlock'>
{data.map((todo: { id: string; title: string }) =>
<div key={todo.id}>{todo.id} {todo.title} </div>)}
</div>
</>
);
};
I use it in such a way that MyDivBlock is nested as a child of
const App: NextPage = () => {
return (
<div>
<Box >
<MyDivBlock key="key0" areaText="DIV1" another="another"/>
</Box>
</div>
)
}
Note that MyDivBlock is nested in Box and MyDivBlock has no ref attribute. This is important because I need to write Box code with no additional requirements for my nested children. And anyone who will use my Box should not think about constraints and ref attributes.
Then I need to get the dimensions of MyDivBlock in the code of Box component, and later attach some event listeners to it, such as scrolling. These dimensions and listeners will be used in the Box component. I wanted to use Ref to control it. That is, the Box will later observe changes in the dimensions and events of MyDivBlock by creating a ref-reference to them
I know that this kind of parent-child relationship architecture is implemented through forwardRef
And here is the Box code:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
export interface BoxProps extends React.ComponentProps<any> {
children?: Element[];
className: string;
}
export const Box: React.FC<BoxProps> = ({ children, ...rest }: BoxProps): JSX.Element => {
const childRef = useRef<HTMLDivElement>();
const ChildWithForwardRef = forwardRef<HTMLDivElement>((props, _ref) => {
const methods = {
show() {
if (childRef.current) {
console.log("childRef.current is present...");
React.Children.forEach(children, function (item) {
console.log(item)})
console.log("offsetWidth = " + childRef.current.offsetWidth);
} else {
console.log("childRef.current is UNDEFINED");
}
},
};
useImperativeHandle(_ref, () => (methods));
return <div ref={childRef}> {children} </div>
});
ChildWithForwardRef.displayName = 'ChildWithForwardRef';
return (
<div
className={'BoxArea'}>
<button name="ChildComp" onClick={() => childRef.current.show()}>get Width</button>
<ChildWithForwardRef ref={childRef} />
</div>
);
}
export default Box;
The result of pressing the button:
childRef.current is present...
[...]
$$typeof: Symbol(react.element) key: "key0" props: {areaText: 'DIV1', another: 'another'}
[...] Object
offsetWidth = undefined
As you can see from the output, the component is visible through the created ref. I can even make several nested ones and get the same for all of them.
But the problem is that I don't have access to the offsetWidth and other properties.
The other challenge is how can I add the addEventListener?
Because it works in pure Javascript with their objects like Element, Document, Window or any other object that supports events, and I have ReactChildren objects.
Plus I'm using NextJS and TypeScript.
Didn't dive too deep into the problem, but this may be because you are passing the same childRef to both div inside ChildWithForwardRef and to ChildWithForwardRef itself. The latter overwrites the former, so you have the method .show from useImperativeHandle available but not offsetWidth. A quick fix is to rewrite ChildWithForwardRef to use its own ref:
const ChildWithForwardRef = forwardRef<HTMLDivElement>((props, _ref) => {
const ref = useRef<HTMLDivElement>()
const methods = {
show() {
if (ref.current) {
console.log("ref.current is present...");
React.Children.forEach(children, (item) => console.log(item))
console.log("offsetWidth = " + ref.current.offsetWidth);
} else {
console.log("ref.current is UNDEFINED");
}
},
};
useImperativeHandle(_ref, () => (methods));
// Here ref instead of childRef
return <div ref={ref}> {children} </div>
});
But really I don't quite get why you would need ChildWithForwardRef at all. The code is basically equivalent to this simpler version:
const Box: React.FC<BoxProps> = ({ children, ...rest }: BoxProps): JSX.Element => {
const childRef = useRef<HTMLDivElement>();
const showWidth = () => {
if(childRef.current) {
console.log("childRef.current is present...");
React.Children.forEach(children, item => console.log(item))
console.log("offsetWidth = " + childRef.current.offsetWidth);
} else {
console.log("childRef.current is UNDEFINED");
}
}
return (
<div className={'BoxArea'}>
<button name="ChildComp" onClick={showWidth}>get Width</button>
<div ref={childRef}>{children}</div>
</div>
);
}
You can't solve this completely with React. I solved it by wrapping the child component, making it take the form of the parent.

Element type is invalid: expected a string (for built-in components), the way I do the import seems good

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
I know this error is quite common and easy to fix but I still cannot find my error.
I got this element when I include the component ProcessModal
on my component
export const CreateProcessButton = ({ selectedIds }) => {
const [showProcessModal, setShowProcessModal] = useState(false);
// const refresh = useRefresh();
// const notify = useNotify();
// const unselectAll = useUnselectAll();
return (
<Button
label="Dupliquer la séléction sur ..."
onClick={() => setShowProcessModal(true)}
>
<ProcessModal open={showProcessModal} {...selectedIds}/>
{/*</ProcessModal>*/}
<SyncIcon />
</Button>
);
};
I import ProcessModal this way
import ProcessModal from './processModal';
from the file processModal.jsx located in the same directory.
The content of processModal.jsx :
import React, {useState} from "react";
import Modal from 'react-modal';
const ProcessModal = (selectedIds, open) => {
const customStyles = {
content : {
top : '50%',
left : '50%',
right : 'auto',
bottom : 'auto',
marginRight : '-50%',
transform : 'translate(-50%, -50%)'
}
};
var subtitle;
const [showProcessModal,setShowProcessModal] = useState(open);
function afterOpenModal() {
// references are now sync'd and can be accessed.
subtitle.style.color = '#f00';
}
function closeModal(){
setShowProcessModal(false);
}
return (
<Modal
isOpen={showProcessModal}
onAfterOpen={afterOpenModal}
onRequestClose={closeModal}
style={customStyles}
contentLabel="Example Modal"
>
<h2 ref={_subtitle => (subtitle = _subtitle)}>Hello</h2>
<button onClick={closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
{/*<SelectField choices={[*/}
{/* { id: 'M', name: 'Male' },*/}
{/* { id: 'F', name: 'Female' }*/}
{/*]}*/}
/>
</Modal>
);
};
export default ProcessModal;
Do you know why I have this error, or in which way I can find the problem since the error gives me no indication.
React components expect a single props object as a parameter. What you have done is a bit strange in how you define your PostModal component signutare as it is neither expecting a single porps object or destructing it to inside variables but instead expects two arguments.
Try this:
// here we destruct the props object in the variables you expect being inside
const ProcessModal = ({ selectedIds, open }) => { ... }
When using the component also try this:
<ProcessModal open={showProcessModal} selectedIds={selectedIds}/>

React Cannot read property 'getAttribute' of undefined

I get the following error when trying to compile my app TypeError: Cannot read property 'getAttribute' of undefined.
I'm having trouble tracking down why in this line: if (event.target.getAttribute('name') && !sectionsClicked.includes(event.target.getAttribute('name'))) { is error
Here is the main react component
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
sectionsClicked: [],
};
this.handleProgress = this.handleProgress.bind(this);
}
handleProgress = (event) => {
const { progressValue } = this.state;
console.log(progressValue);
const { sectionsClicked } = this.state;
if (event.target.getAttribute('name') && !sectionsClicked.includes(event.target.getAttribute('name'))) {
this.setState({
progressValue: Math.floor((sectionsClicked.length / 77) * 100),
sectionsClicked: sectionsClicked.push(event.target.getAttribute('name')),
});
console.log(sectionsClicked);
}
};
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
return (
<>
<Progress value={progressValue} />
<div className="text-center mt-5">
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={(event) => this.handleProgress(event)}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
As per the docs:
https://github.com/final-form/react-final-form#onchange-formstate-formstate--void
The expected param passed to onChange is formState not event. Try logging the parameter passed to the function it might have the changes you need.
The <FormSpy> component that originally calls onChange in <Wizard>, which you report upwards to <App>, passes a parameter that is not an event. It's of type FormState.
On that type, the target property is undefined, thus the error you're seeing.
If you see the properties of that object in the documentation, you'll probably get what you want by checking the dirty fields of the form, and finding the associated DOM elements.
In all likelihood, though, you can find a way of achieving the desired behavior without resorting to DOM attributes at all. The very same dirtyFields property has the names you might be looking for.
Also, take a look at your methods and binding. You're actually using three separate approaches to fix the context of your handleProgress function.
For more information, look at this answer:
Can you explain the differences between all those ways of passing function to a component?

React error - Cannot assign to read only property 'validated' of object '#<Object>'

I have a the following component:
export const Alert = (props: {message: string, type?: AlertType, createAgainButton?: Object} ) =>
{
const type = props.type || 'Message'
switch(type)
{
case 'Warning':
return (
<div className='tgg-alert-danger'>
<div className='tgg-alert-icon'><Icon name='exclamation-circle'/></div>
<div className='tgg-alert-text'>{props.message}</div>
</div> )
case 'Spinner':
return (
<div className='tgg-alert-danger'>
<div className='tgg-alert-icon'><Icon name='circle-o-notch fa-spin'/></div>
<div className='tgg-alert-text'>{props.message}</div>
</div>)
default:
return (
<div className='tgg-alert-success'>
<div className='tgg-alert-icon'><Icon name='check-circle'/></div>
<div className='tgg-alert-text'>{props.message}</div>
{ props.createAgainButton }
</div>)
}
}
For the default case, sometimes my code raises the following React error:
Cannot assign to read only property 'validated' of object '#<Object>'
The error is reported to be located in a React module:
TypeError: Cannot assign to read only property 'validated' of object '#
<Object>'
validateChildKeys
node_modules/react/cjs/react.development.js:1136
1133 | } else if (isValidElement(node)) {
1134 | // This element was passed in a valid location.
1135 | if (node._store) {
> 1136 | node._store.validated = true;
1137 | }
1138 | } else if (node) {
1139 | var iteratorFn = getIteratorFn(node);
And I'm not sure why. I've tried changing the penultimate expression to :
{ props.createAgainButton && props.createAgainButton }
Here's where I use the Alert component in this case:
// #flow
import React from 'react'
import {connect} from 'react-redux'
import {Alert} from '../../ui/coreUIComponents'
import type {AlertType} from '../misc/types'
const mapStateToProps = (state) => {
return {
message: state.alert.message,
danger: state.alert.danger,
createAgainButton: state.alert.createAgainButton
}
}
export default connect(mapStateToProps)(
(props: {message: string, danger: boolean, createAgainButton?: Object}) =>
{
window.scrollTo(0,0);
const type: AlertType = props.danger? "Warning" : "Message"
return (
props.message ? <div>
{ props.createAgainButton ?
<Alert message={props.message} type={type} createAgainButton={props.createAgainButton} />
:
<Alert message={props.message} type={type} />
}
</div>
:
<div></div>
)
}
)
I wonder if there is anything obvious I'm doing wrong. The alert is shown after an async call to upload data. Sometimes, it needs to show a button to create another record. The functionality is provided by a HoF that wraps underlying record components. It works for some, but not others. Any ideas?
Phil
I know it's an old question, but maybe someone finds it nowadays. We had something similar happening in our codebase. I just want to mention what the cause for us was.
Briefly, there were react JSX elements stored in the state, and we were using immer which essentially deep-freezes everything in the store, thus the component._store property also got set to read only - which caused the development version of react to fail on the child-key validation step.
As I can see, you're also storing the createAgainButton somewhere, maybe it's coming from the store, where one of your middlewares could have made it immutable.
Solution: Don't store JSX in your store. If you have to, then don't use immer, but if you want to, then don't use development version of react.

Resources