Started seeing this one last night, I need to try and isolate it a bit more.
Getting this error. Specifically, it seems to have after the second save on a certain page. Never the first save, and other pages are working normally.
Uncaught TypeError: Cannot read property 'propTypes' of undefined
at updateMemoComponent (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:14308)
at beginWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:15486)
at performUnitOfWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:19109)
at workLoop (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:19149)
at renderRoot (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:19232)
at performWorkOnRoot (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:20139)
at performWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:20051)
at performSyncWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:20025)
at requestWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:19894)
at scheduleWork (webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:19708)
webpack-internal:///../../node_modules/@hot-loader/react-dom/cjs/react-dom.development.js:16914 The above error occurred in the <InnerNotFoundBoundary> component:
in InnerNotFoundBoundary (created by Context.Consumer)
in ErrorBoundary (created by Layout)
in div (created by ForwardRef(Gloss))
in ForwardRef(Gloss)
in ForwardRef(Gloss) (created by Layout)
in SimpleProvider (created by Layout)
in ThemeByName (created by Theme)
in Theme (created by Layout)
in ThemeProvide
in Provider
in Provider
in Provider
in Provider
in Unknown (created by Layout)
in Layout
in NaviProvider (created by Router)
in Router
in Unknown (created by SiteRoot)
in AppContainer (created by SiteRoot)
in SiteRoot
React Hot Loader version: 4.8.4
Have the @hot-loader dom patch, and the following settings:
setConfig({
logLevel: 'no-errors-please',
pureSFC: true,
pureRender: true,
// disableHotRenderer: true,
})
The page that it's running on is a bit large, I'll just paste it here anyway:
import {
BorderRight,
Button,
Col,
gloss,
Input,
List,
Portal,
RoundButton,
Row,
Section,
Sidebar,
SpaceGroup,
SubTitle,
SurfacePassProps,
Toolbar,
useMedia,
} from '@o/ui'
import { compose, mount, route, withView } from 'navi'
import React, { memo, useEffect, useRef, useState } from 'react'
import { useNavigation, View } from 'react-navi'
import { useScreenSize } from '../hooks/useScreenSize'
import { useSiteStore } from '../Layout'
import { Header } from '../views/Header'
import { MDX } from '../views/MDX'
import { SectionContent } from '../views/SectionContent'
import DocsInstall from './DocsPage/DocsInstall.mdx'
const views = {
install: () => import('./DocsPage/DocsInstall.mdx'),
buttons: () => import('./DocsPage/DocsButtons.mdx'),
cards: () => import('./DocsPage/DocsCards.mdx'),
progress: () => import('./DocsPage/DocsProgress.mdx'),
lists: () => import('./DocsPage/DocsLists.mdx'),
start: () => import('./DocsPage/DocsStart.mdx'),
}
export default compose(
withView(req => {
const id = req.path.slice(1)
return (
<DocsPage id={id}>
<View />
</DocsPage>
)
}),
mount({
'/': route({
title: 'Docs',
view: <DocsInstall />,
}),
'/:id': route(async req => {
let id = req.params.id
if (!views[id]) {
return {
view: () => <div>not found</div>,
}
}
let ChildView = (await views[id]()).default || (() => <div>nada {id}</div>)
return {
view: <ChildView />,
}
}),
}),
)
function DocsPage(props: { id?: string; children?: any }) {
const screen = useScreenSize()
const itemIndex = categories.all.findIndex(x => x['id'] === props.id) || 1
const item = categories.all[itemIndex]
const siteStore = useSiteStore()
const [showSidebar, setShowSidebar] = useState(true)
const [section, setSection] = useState('all')
const toggleSection = val => setSection(section === val ? 'all' : val)
const nav = useNavigation()
const [search, setSearch] = useState('')
const inputRef = useRef(null)
const content = (
<React.Fragment key="content">
<DocsToolbar section={section} toggleSection={toggleSection} />
<List
search={search}
selectable
alwaysSelected
defaultSelected={itemIndex}
items={categories[section]}
onSelect={rows => {
nav.navigate(`/docs/${rows[0].id}`, { replace: true })
}}
/>
</React.Fragment>
)
const isSmall = screen === 'small'
useEffect(() => {
const keyPress = e => {
// console.log('e', e.keyCode)
switch (e.keyCode) {
case 84: // t
inputRef.current.focus()
break
}
}
window.addEventListener('keydown', keyPress)
return () => {
window.removeEventListener('keydown', keyPress)
}
}, [])
useEffect(() => {
inputRef.current && inputRef.current.focus()
}, [inputRef.current])
return (
<MDX>
<Portal prepend style={{ position: 'sticky', top: 10, zIndex: 10000000 }}>
<Row margin={[0, 'auto']} pointerEvents="auto" pad={['sm', 100]} width="90%" maxWidth={800}>
<Input
ref={inputRef}
onChange={e => setSearch(e.target.value)}
flex={1}
sizeRadius={10}
size="xl"
iconSize={16}
icon="search"
placeholder="Search the docs..."
elevation={5}
after={
<Button tooltip="Shortcut: t" size="sm" alt="flat" fontWeight={600}>
t
</Button>
}
/>
</Row>
</Portal>
<Portal prepend>
<Header slim />
</Portal>
<Portal>
<FixedLayout>
{isSmall ? (
<Sidebar
hidden={!showSidebar}
zIndex={10000000}
elevation={5}
pointerEvents="auto"
// @ts-ignore
background={theme => theme.background}
>
{content}
</Sidebar>
) : (
<SectionContent pointerEvents="none" flex={1}>
<Col position="relative" flex={1} width={300} pointerEvents="auto">
{content}
<BorderRight opacity={0.5} />
</Col>
</SectionContent>
)}
</FixedLayout>
</Portal>
<SectionContent fontSize={16} lineHeight={28}>
<ContentPosition isSmall={isSmall}>
<DocsContents
onToggleSidebar={() => setShowSidebar(!showSidebar)}
setTheme={siteStore.setTheme}
theme={siteStore.theme}
title={item ? item['title'] : undefined}
>
{props.children}
</DocsContents>
</ContentPosition>
</SectionContent>
</MDX>
)
}
DocsPage.theme = 'light'
const ContentPosition = gloss<{ isSmall?: boolean }>({
width: '100%',
padding: [0, 0, 0, 300],
isSmall: {
padding: [0, 0, 0, 0],
},
})
const FixedLayout = gloss({
position: 'fixed',
top: 100,
left: 0,
width: '100%',
height: '100%',
zIndex: 100000,
})
const DocsToolbar = memo(({ section, toggleSection }: any) => {
return (
<Toolbar background="transparent" pad="xs" justifyContent="center" border={false}>
<RoundButton
alt={section === 'docs' ? 'selected' : 'flat'}
onClick={() => toggleSection('docs')}
>
Docs
</RoundButton>
<RoundButton alt={section === 'ui' ? 'selected' : 'flat'} onClick={() => toggleSection('ui')}>
UI
</RoundButton>
<RoundButton
alt={section === 'kit' ? 'selected' : 'flat'}
onClick={() => toggleSection('kit')}
>
Kit
</RoundButton>
</Toolbar>
)
})
const DocsContents = memo(({ setTheme, theme, title, onToggleSidebar, children }: any) => {
const isSmall = useMedia({ maxWidth: 700 })
return (
<Section
maxWidth={600}
margin={[0, 'auto']}
pad={['xl', 'xl', true, 'xl']}
titleBorder
space
title={title || 'No title'}
titleSize="xl"
afterTitle={
<SurfacePassProps iconSize={12}>
<SpaceGroup space="xs">
<Button
icon="moon"
tooltip="Toggle dark mode"
onClick={() => setTheme(theme === 'home' ? 'light' : 'home')}
/>
{isSmall && (
<Button icon="panel-stats" tooltip="Toggle menu" onClick={onToggleSidebar} />
)}
</SpaceGroup>
</SurfacePassProps>
}
>
{children}
</Section>
)
})
const titleItem = { titleProps: { size: 1.1 } }
const ListSubTitle = gloss(SubTitle, {
margin: [20, 0, -2],
fontWeight: 300,
fontSize: 18,
})
const docsItems = [
{
selectable: false,
hideBorder: true,
children: <ListSubTitle>Start</ListSubTitle>,
},
{
id: 'start',
title: 'Getting started',
...titleItem,
},
]
const uiItems = [
{
selectable: false,
hideBorder: true,
children: <ListSubTitle>User Interface</ListSubTitle>,
},
{ id: 'lists', title: 'Lists', icon: 'th-list', group: 'Collections' },
{ id: 'tables', title: 'Tables', icon: 'th' },
{ id: 'tree', title: 'Tree', icon: 'diagram-tree' },
{ id: 'treeList', title: 'TreeList', icon: 'chevron-right' },
{ id: 'definitionList', title: 'DefinitionList', icon: 'list-columns' },
{
group: 'Views',
id: 'surfaces',
icon: 'layer',
title: 'Surface',
subTitle: 'Building block of many views',
},
{ id: 'icons', icon: 'star', title: 'Icons', indent: 1 },
{ id: 'buttons', icon: 'button', title: 'Buttons', indent: 1 },
{ id: 'cards', title: 'Cards', icon: 'credit-card', indent: 1 },
{ id: 'install', title: 'Sections', icon: 'application' },
{ id: 'install', title: 'Popovers', icon: 'direction-right' },
{ id: 'install', title: 'Decorations', icon: 'clean' },
{ id: 'progress', title: 'Progress', icon: 'circle' },
]
const categories = {
all: [...docsItems, ...uiItems],
ui: uiItems,
docs: docsItems,
kit: uiItems,
}
Seems like what's tripping it up is the <ListSubTitle>Start</ListSubTitle>, that view is a a memo(forwardRef()) component, possible the usage of it inline in the file.
Sorry - there is no way I could reproduce the problem, but you are trying to memo(undefined)
I'm facing same error when using react.memo!
@theKashey
but you are trying to memo(undefined)
Not exactly... debugger says:
updateMemoComponent(/*...*/) {
// ...
var _type = Component.type; // <-- Component is a function and has no type!
var _innerPropTypes = _type.propTypes; // <-- _type is undefined
if (_innerPropTypes) {
// Inner memo component props aren't currently validated in createElement.
// We could move it there, but we'd still need this for lazy code path.
checkPropTypes(_innerPropTypes, nextProps, // Resolved props
'prop', getComponentName(_type), getCurrentFiberStackInDev);
}
}
For some components it works, but for others - fails. Maybe it depends when memoized component is being used directly with some other HOC.
In my case: WithContext is a HOC, WorstInstruments is a memoized component

And I'm always getting hot-update error.
// withContext
*
* @param {React.Context<any>} Context
* @param [opts = {}]
* @param [opts.mergeProps]
* @returns {function(React.ComponentType<any>): React.ComponentType<any>}
*/
const withContext = (Context, opts = {}) => WrappedComponent => {
const { mergeProps = defaultMergeProps } = opts;
function WithContext(props) {
return (
<Context.Consumer>
{ctx => {
const propsToWrapped = mergeProps(ctx, props);
return <WrappedComponent {...propsToWrapped} />;
}}
</Context.Consumer>
);
}
return hoistNonReactStatics(WithContext, WrappedComponent);
};
Probably I've found a problem. There are two different "memo" components - a MemoComponent, and SimpleMemoComponent.
RHL works for the second one, breaks for the first. I don't get yet, where which one is used(and how to create a test for it), but it's clear how I am breaking it
This issue should be solved simultaneously with #1135, ie since v4.6.3.
I am trying to write a failing test, or at least some example to play with, not it always works :(
PS: testing with react-dom 16.8.6
v4.8.5 should fix this
Most helpful comment
Probably I've found a problem. There are two different "memo" components - a MemoComponent, and SimpleMemoComponent.
RHL works for the second one, breaks for the first. I don't get yet, where which one is used(and how to create a test for it), but it's clear how I am breaking it