Uncaught Error in react component invalid hook, useSWR - javascript

using a useSWR wrapper and I cannot find the issue that is causing the invalid hook error in the Player component.
I went through https://reactjs.org/warnings/invalid-hook-call-warning.html and https://reactjs.org/docs/hooks-rules.html
I didnt have this problem before with react useContext. I refactored and switched to useSWR, router v6. The error is new
error details:
react-dom.development.js:18687 The above error occurred in one of your React components:
...
line 115341
line 115342 e.default = function (n) {
line 115343 var e = n.title,
line 115344 t = void 0 !== e && e,
line 115345 a = n.subTitle,
...
at e.default (http://localhost:3000/static/js/bundle.js:115343:15)
at div
at Player (http://localhost:3000/static/js/bundle.js:8314:108)
at div
at div
at Tab (http://localhost:3000/static/js/bundle.js:6132:104)
at Routes (http://localhost:3000/static/js/bundle.js:122353:5)
at Router (http://localhost:3000/static/js/bundle.js:122286:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:121095:5)
at SWRConfig$1 (http://localhost:3000/static/js/bundle.js:137530:21)
at App (http://localhost:3000/static/js/bundle.js:2528:86)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
react-dom.development.js:12056 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
at resolveDispatcher (react.development.js:1476:1)
at useRef (react.development.js:1515:1)
at e.default (ReactNetflixPlayer.js:1:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at beginWork$1 (react-dom.development.js:27426:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)
here is the package.json
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-netflix-player": "^1.1.7",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1",
"redux": "^4.2.0",
"swr": "^1.3.0"
},
here is the code
// useSharedState.js
---
import useSWR from 'swr'
export const useSharedState = (key, initial) => {
const { data: state, mutate: setState } = useSWR(key, {
fallbackData: initial,
})
return [state, setState]
}
// Container.js
---
import { useSharedState } from "../../hooks/useSharedState"
import useSWR from 'swr'
function Container() {
const { data: item } = useSWR(`/endpoint`)
return (
item && (
<div><button
onClick={() => {setPlayerItem(item) }} ... >
...</div>
);
}
export default Container;
// Player.js
---
import React, { useEffect, useContext } from 'react';
import ReactNetflixPlayer from "react-netflix-player";
const Player = () => {
const [playerItem, setPlayerItem] = useSharedState('playerContext')
return (
<React.Fragment>
{playerItem && (
<div id={"player"} >
<ReactNetflixPlayer
title={playerItem.title}
backButton={() => { setPlayerItem(null) }}
/>
</div>
)}
</React.Fragment>
);
}
export default Player;
// Tab.js
---
import Player from "../Player"
...
function Tab() {
return (
<div>
<Player />
<React.Fragment>
<Container />
{columnData && <BodyColumn data={columnData}/>}
...
</React.Fragment>
</div>
);
}
export default Tab;
<Tab> is a component in <App>
The Player component is rendered after a button is clicked that sets data
there are other components with buttons that perform a click and set state by referencing const [playerItem, setPlayerItem] = useSharedState('playerContext')
thanks in advance
related invalid hook call with SWR library
edit: https://codesandbox.io/s/invalid-hook-debugging-2v37wj

Related

The difference between the two export ways when use observer from react-mobx?

the demo url : https://github.com/allSmi/cra-app
"mobx": "^6.6.1",
"mobx-react": "^7.5.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
bad
const App = function () {
// toggle comments this line and save, the console will log error
// const [test, setTest] = useState(0);
return (
<div>...</div>
)
}
export default observer(App);
the first way, when add or delete the hooks, after save, the console will log error
error info image
Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
Uncaught Error: Rendered more hooks than during the previous render.
good
const App = observer(function () {
// toggle comments this line and save, isOk
// const [test, setTest] = useState(0);
return (
<div>...</div>
)
})
export default App;
What is the difference between the two ways?

Invalid Hook Call error: works on localhost but not with Jest

First I thought my React and React-dom were not in the same version. But I checked and they were all in the same version. The component itself renders and works properly in localhost, but when I test it using Jest it fails. Below is the error I get, and the location of error is in the .create method. Also, the tests pass when I don't pass in any child components. Both examples shown below. This is so confusing and I have no clue why the error is prompting. Any suggestions?
Edit: changed question format to show error better
To reproduce: npm run test
The Error
● Navbar component › Rendered navbar with 1 tab
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
61 | test('Rendered navbar with 1 tab', () => {
62 | const tree = renderer
> 63 | .create(
| ^
64 | <Navbar>
65 | <Navbar.Tab label="In Flight Orders" component={null} />
66 | </Navbar>
at resolveDispatcher (../../../node_modules/react/cjs/react.development.js:1465:13)
at Object.useState (../../../node_modules/react/cjs/react.development.js:1496:20)
at useForceUpdate (../../../node_modules/framer-motion/dist/framer-motion.cjs.js:5707:33)
at AnimatePresence (../../../node_modules/framer-motion/dist/framer-motion.cjs.js:5841:23)
at renderWithHooks (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5552:18)
at mountIndeterminateComponent (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7918:13)
at beginWork (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9019:16)
at performUnitOfWork (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12649:12)
at workLoopSync (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12622:22)
at performSyncWorkOnRoot (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12333:9)
at ../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1825:24
at unstable_runWithPriority (../../node_modules/scheduler/cjs/scheduler.development.js:653:12)
at runWithPriority (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1775:10)
at flushSyncCallbackQueueImpl (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1820:7)
at flushSyncCallbackQueue (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1808:3)
at scheduleUpdateOnFiber (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11776:9)
at updateContainer (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14747:3)
at Object.create (../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15455:5)
at Object.<anonymous> (src/lib/navbar.spec.js:63:8)
This works
test('Rendered navbar with many non tabs', () => {
const tree = renderer
.create(
<Navbar>
<h1>non tab 1</h1>
<h1>non tab 2</h1>
</Navbar>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
This doesn't work
test('Rendered navbar with filter', () => {
const tree = renderer
.create(
<Navbar filter>
<Navbar.Tab label="In Flight Orders" component={null} />
<Navbar.Tab label="Active Service" component={null} />
<Navbar.Tab label="Active Service 1" component={null} />
</Navbar>
).toJSON();
expect(tree).toMatchSnapshot();
});
Navbar.js
import React, { useState, useEffect, useRef } from 'react';
import Tab from './tab';
import { motion, AnimatePresence } from 'framer-motion';
import { Arrow, ArrowGroup, Spacer, NavbarOutline, Content } from './styledNavbar';
const Navbar = ({children, filter}) => { ... }
const Tabs = () => {
return 'tabs';
};
Navbar.Tab = Tabs;
export default Navbar;

How to properly add and use third party/npm libraries in Reactjs?

I'm learning react and it's my first time using a library in react. I'm trying to use watermark-js. I've read articles about how I to add npm packages in my react package.json using npm install <library name> save. Here is the part of package.json which show the watermarkjs
"dependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "2.1.8",
"save": "^2.4.0",
"watermarkjs": "^2.0.0"
Now in my App.js component I've imported it as a module and use the snippet provided by watermarkjs in componentDidMount method as in react documentation. below is my complete App.js component.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Form from './Components/Form';
import ImageWaterMark from './Components/ImageWaterMark';
import image1 from './test-image.jpg'
import image2 from './download.jpg'
import { watermark } from 'watermarkjs'
class App extends Component {
componentDidMount() {
//Snippit
watermark([{ image1 }, { image2 }])
.image(watermark.image.lowerRight(0.5))
.then(function (img) {
document.getElementById('lower-right').appendChild(img);
});
}
render() {
return (
<div className="App">
<div ref="water"></div>
</div>
);
}
}
export default App;
There are 2 problem that I can't seem to understand. first
How can I pass this snippet as a ref to the render method as in
documentation?
Second the App through the following error
TypeError: Cannot read property 'watermark' of undefined
I've read many articles and watched videos but I haven't understand the concept of using libraries. Any help would be much appreciated.
watermarkjs uses a default export, not a named export. Change this:
import { watermark } from 'watermarkjs'`
To this:
import watermark from 'watermarkjs'
As for how to use it, once it loads you should call setState with the result. This will render your component again, and you can use that value however you see fit. For example:
componentDidMount() {
watermark([{ image1 }, { image2 }])
.image(watermark.image.lowerRight(0.5))
.then((img) => {
this.setState({ img });
});
}
render() {
return (
<div className="App">
<div ref="water">
{this.state.img && /* whatever you want to do with the image */ }
</div>
</div>
);
}
I'm not exactly sure what img resolves to, so don't know exactly what you'd put in the render function. If it's url string, it would be {this.state.img && <image src={this.state.img} />}

Cannot read property 'replaceChild' of null URL: webpack:///./~react-dom/lib/DOMLazyTree.js

I am using phonegap to create an app using react v15 and "webpack": "^2.2.1" to bundle up everything. I then run my webpack.prod.json to generate a stand alone .js file which is included in the app.
When I install the app locally, I have no problems and can run the application, however when I build through phonegap.com/build is when the issue occurs.
Even weirder still is that the initial state loads fine. Its when I navigate to #/home that this error occurs:
Uncaught SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at RequireLogin.render (RequireLogin.js:32)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (ReactCompositeComponent.js:799)
at ReactCompositeComponentWrapper._renderValidatedComponent (ReactCompositeComponent.js:822)
at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:362)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:258)
at Object.mountComponent (ReactReconciler.js:46)
at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:371)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:258)
at Object.mountComponent (ReactReconciler.js:46)
RequireLogin.js
import React from 'react';
import {connect} from 'react-redux';
import {Redirect} from 'react-router'
class BlockForLoggedInUsers extends React.Component {
constructor(props){
super(props);
}
render(){
const loggedIn = this.props.user.loggedIn || JSON.parse(localStorage.LOGGED_IN);
if (loggedIn) {
return (
<Redirect push to="/home"/>
)
}else{
return (
<div></div>
);
}
}
}
function mapStateToProps(state){
return {
user: state.user
};
}
export default connect(mapStateToProps)(BlockForLoggedInUsers);
Then, when I hit the back key I get a different error:
DOMLazyTree.js:69 Uncaught TypeError: Cannot read property 'replaceChild' of null
at Function.replaceChildWithTree (DOMLazyTree.js:69)
at Object.dangerouslyReplaceNodeWithMarkup [as replaceNodeWithMarkup] (Danger.js:41)
at ReactCompositeComponentWrapper._replaceNodeWithMarkup (ReactCompositeComponent.js:784)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:774)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:724)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:645)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:547)
at Object.receiveComponent (ReactReconciler.js:125)
at Object.updateChildren (ReactChildReconciler.js:109)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:213)
The problem I have with this is it means that the app is loading well, but I assume something in my #/home component is broken, but I have stripped this back to be nothing but a div.
All packages noteworthy:
"history": "^4.5.1",
"react": "^15.5.0",
"react-css-modules": "^4.1.0",
"react-dom": "^15.5.0",
"react-redux": "^5.0.3",
"react-router": "^4.1.1",
"react-router-dom": "^4.0.0",
"react-router-redux": "^5.0.0-alpha.6",
"react-tap-event-plugin": "^2.0.1",
"redux": "^3.6.0",
"webpack": "^2.2.1"
Router.js
export default class Routes extends Component {
render() {
return (
<div>
<Route exact path="/" component={Splash}/>
<Route path="/signin" component={SignIn} />
<Route path="/logout" component={Logout} />
<Route path="/home" component={Home} />
<Route path="/settings" component={Settings} />
<Route path="/sextypeselection" component={SexTypeSelection} />
<Route path="/desire" component={Desire} />
</div>
)
}
};
home/index.js
import React from 'react';
import { bindActionCreators } from 'redux'
import { RequireLogin } from '../shared/auth/userRedirects'
import mobiscroll from '../shared/mobiscroll/mobiscroll.custom';
import * as currentSexInfo from '../../actions/currentSexInfo'
import {connect} from 'react-redux';
//Header
import Header from '../shared/header/Header';
import RightPlus from '../shared/header/RightPlus';
import Menu from '../shared/menu';
//Styles
import HomeStyle from './home.css';
class Home extends React.Component {
constructor (props){
super(props);
console.info(props);
const now = new Date();
this.state = {
settings : {
display: 'inline',
yearChange: false,
marked: [
new Date(2017, 5, 4)
]
, max: new Date()
// , showOuterDays: false
}
};
this.selectData = this.selectData.bind(this);
}
selectData = (event, inst) => {
if(event.control) {
this.props.DispatchChangeCurrentSexInfo(event.date);
window.location.hash = 'sextypeselection';
}
};
onPosition = (ev) => {
let $ = mobiscroll.$,
monthCont = $('.mbsc-cal-anim-c', ev.target),
calHeight = document.body.offsetHeight - 375;
monthCont.height('');
monthCont.height(calHeight);
};
render(){
return (
<div>
<RequireLogin />
<Header right={<RightPlus link="sextypeselection" />} />
<div className={HomeStyle.home}>
<div className="pageInfo">
<h1>My calender</h1>
<p>Select a date to view <br/> input your information</p>
</div>
<div className={"cal " + HomeStyle.calenderContainer}>
<mobiscroll.Calendar onPosition={this.onPosition} onSetDate={this.selectData} options={this.state.settings} {...this.props} />
</div>
</div>
<Menu />
</div>
);
}
}
function matchDispatchToProps(dispatch){
return {DispatchChangeCurrentSexInfo : bindActionCreators(currentSexInfo.changeCurrentSexInfo, dispatch)}
}
export default connect(null,matchDispatchToProps)(Home);
Sadly it was simple in the end, I doubt anybody will have this same problem but in the end it was JSON.parse trying to work on an undefined item.
The fix:
const storage = localStorage.LOGGED_IN || {};
const loggedIn = this.props.user.loggedIn || JSON.parse(JSON.stringify(storage));
Here
In react-domlibrary >>
DOMLazyTree.js
oldNode get in null, and hence parentNode get in undefind for replaceChild function.
function replaceChildWithTree(oldNode, newTree) {
oldNode.parentNode.replaceChild(newTree.node, oldNode);
insertTreeChildren(newTree);
}
So make sure oldNode.parentNode would be fulfilled with required data.

How to simulate an event on a unit test with Jest, Enzyme for React-Native

I'm trying to figure out how to test an "onPress" event with Jest in a React-Native app so I can make sure the right function is called.
I went through the documentation and Google but couldn't find a solution for it in React-Native.
This is what I found that is supposed to work for React-Native with enzyme:
const mockFunc = jest.fn();
const component = mount(<MyComponent onPress={mockFunc} />);
component.simulate('press');
expect(mockFunc).toHaveBeenCalled();
But this doesn't work. Seems like mount doesn't work and I get this output:
ReferenceError: document is not defined
I tried with shallow instead but the TouchableOpacity is not getting rendered when I look at the output of the function... and you've guessed it, it doesn't work either. Not sure what to do.
Does anyone found a way to test events on React-Native?
Thanks
Enzyme does not support React-Native, because it's rendered differently and doesn't use the DOM. That's why you're getting the error ReferenceError: document is not defined. You can see this issue for more information. The React team is currently working to expose a .find() method in react-test-renderer to simulate actions on components. Then it should work for both React/React-native without needing a DOM environment.
There's a hack you can do (and that's what we did in our company) that is rendering a custom component that extends TouchableOpacity and map onClick to call onPress. Something like this:
const mockPressable = (name) => {
const RealComponent = require.requireActual(name);
class Component extends RealComponent {
render() {
return React.createElement(
RealComponent.displayName || RealComponent.name,
{ ...this.props, onClick: this.props.onPress },
this.props.children
);
}
}
return Component;
};
jest.mock('TouchableOpacity', () => mockPressable('TouchableOpacity'));
And in your test code, you call component.simulate('click').
It's a hack and I'm not sure what are the consequences of doing this but it has worked for our use cases.
You should use shallow instead, then called .dive()
const mockFunc = jest.fn();
const component = shallow(<MyComponent onPress={mockFunc} />);
component.dive().simulate('press');
expect(mockFunc).toHaveBeenCalled();
I'm able to run tests like what you've described in your question in React Native. Here is my configuration:
package.json
"scripts": {
...
"test": "node_modules/jest/bin/jest.js",
}
"devDependencies": {
...
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.1",
"enzyme-to-json": "^3.1.2",
"jest": "^21.2.1",
"jest-enzyme": "^4.0.0",
"jest-expo": "~21.0.0",
}
"jest": {
"preset": "jest-expo",
"setupFiles": [
"./test/jestSetup.js"
],
"snapshotSerializers": [
"./node_modules/enzyme-to-json/serializer"
]
}
test/jestSetup.js
import { configure, shallow, render, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
configure( { adapter: new Adapter() } )
// enzyme
global.shallow = shallow
global.render = render
global.mount = mount
Example component:
import React from 'react'
import { Button } from 'react-native'
const CancelButton = ( props ) =>
<Button
{ ...props }
onPress={ () => { props.navigation.goBack() } }
title="Cancel"
/>
export { CancelButton }
Example test
import React from 'react'
import { CancelButton } from '../CancelButton'
test( 'onPress', () => {
const goBackFunc = jest.fn()
const navigation = {
goBack: goBackFunc,
}
const component = shallow(
<CancelButton
navigation={ navigation }
/>
)
component.simulate( 'press' )
expect( goBackFunc ).toHaveBeenCalled()
} )
.babelrc
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": ["transform-react-jsx-source"]
}
}
}

Categories

Resources