How to fix 'Invariant Violation' caused by cyclic dependencies in react-redux - javascript

I have a React project set up like this:
It is a simple application. The Dashboard has a UserListContainer, containing a UserList, which lists four users with their ID and name. The UserList gets the Users from Data.ts
The application itself works just fine and displays the four users. But as soon as I try to test the UserList with enzymes shallow rendering, the tests give me the following error message:
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
at invariant (node_modules/invariant/invariant.js:40:15)
at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:97:33)
at Object.<anonymous> (src/Users/UserListContainer.tsx:4:34)
at Object.<anonymous> (src/Users/index.ts:1:1)
at Object.<anonymous> (src/Dashboard/Dashboard.tsx:2:1)
at Object.<anonymous> (src/Dashboard/index.ts:1:1)
at Object.<anonymous> (src/Users/UserList.tsx:2:1)
at Object.<anonymous> (src/Users/__tests__/UserList.test.tsx:3:1)
The problem is basically that, even though we don't use the Dashboard when rendering the UserList shallowly, React still tries to build it. I guess that happens because we access Data through the Dashboard index, so React will also try to resolve Dashboard and its imports, namely UserListContainer, because they are exported through the same index file. When I import the users directly instead of through the index, the problem disappears.
We fixed this issue by breaking the cyclic dependency but if I encounter the error again, I want to know other ways to fix it. I would also like to understand why the web application still seems to be working just fine, while the tests fail.
Also, is there a way to prevent React from resolving the imports and exports when using enzymes shallow rendering?
Users/__tests__/UserList.test.tsx
test("reproduce the problem", () => {
const wrapper = shallow(<UserList />)
console.log(wrapper)
expect(1).toBe(1)
})
Users/UserList.tsx
import { Data } from "../Dashboard"
export const UserList: React.FC = () => (
<React.Fragment>
{Data.users.map(user => (
<div>
<code>{user.id} - </code>
<code>{user.name}</code>
</div>
))}
</React.Fragment>
)
Dashboard/index.ts
export { Dashboard } from "./Dashboard" // not used but still resolved
export { Data } from "./Data" // actually used
Dashboard/Data.ts
export const Data = {
users: [
{ id: "user1", name: "Albert" },
{ id: "user2", name: "Bertha" },
{ id: "user3", name: "Chloe" },
{ id: "user4", name: "Doug" }
]
}
Dashboard/Dashboard.tsx
import { UserListContainer } from "../Users"
export const Dashboard: React.FC = () => {
return <UserListContainer />
}
Users/UserListContainer.tsx
import { UserList } from "./UserList"
export const UserListContainer = connect()(UserList)

One way of fixing it would be to reorder the imports in your dashboard file:
export { Data } from "./Data" // actually used
export { Dashboard } from "./Dashboard" // not used but still resolved
Web application will work in most cases because it will start resolving from your index.tsx file (or whatever your entry file name is) and go from there. Jest on the other hand starts from your test file and only resolves those imports (you can find a nice explanation why this happens here: https://railsware.com/blog/how-to-analyze-circular-dependencies-in-es6/).
We had similar problems in our projects and unfortunatelly except reordering imports and better structuring your files there is no other solution.
One "hack" that you can also do is to add:
import 'problematic_module'
to your jest setupFilesAfterEnv. That way module will be resolved before each test (but i recommend this only as a last resort).

Related

Gatsby page didn't render SSR correctly - Cannot read property 'split' of undefined

I want to make a bilingual form on my website and followed this tutorial. I'm not sure what is undefined here, although I think it has something to do with gatsby-plugin-intl (I thought maybe it's because I don't have a json file for my non-English content, but I added a zh-TW.json and still get the same error. I also get the same error when calling localhost:8000 instead of localhost:8000/contact). The errors only came up after running the npm run extract -- 'src/**/*.js*' --out-file src/intl/en.json --format simple command.
gatsby-config.js
const supportedLanguages = [
{ id: 'en', label: 'English' },
{ id: 'zh-TW', label: '中文 (繁體)' },
]
const defaultLanguage = 'en'
module.exports = {
siteMetadata: {
title: `Gatsby Markdown Blog`,
description: `Learn how to make a blog with Gatsby and Markdown posts.`,
},
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.mdx`, `.md`],
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts`,
},
},
{
resolve: `gatsby-plugin-intl`,
options: {
path: `${__dirname}/src/intl`,
languages: supportedLanguages,
defaultLanguage: defaultLanguage,
redirect: true, // switch to false when zh content ready to prevent gatsby-plugin-intl from auto-redirecting to default language versions
},
},
],
}
browser
React components in Gatsby must render successfully in the browser and in a node.js environment. When we tried to render your page component in node.js, it errored.
URL path: /contact/
File path: undefined
terminal
warn The path "/contact/" errored during SSR.
Edit its component undefined to resolve the error.
ERROR
Cannot read property 'split' of undefined
TypeError: Cannot read property 'split' of undefined
- render-dev-html-child.js:65 parseError
[blog]/[gatsby]/src/utils/dev-ssr/render-dev-html-child.js:65:26
- render-dev-html-child.js:166
[blog]/[gatsby]/src/utils/dev-ssr/render-dev-html-child.js:166:23
- new Promise
- render-dev-html-child.js:135 Object.exports.renderHTML
[blog]/[gatsby]/src/utils/dev-ssr/render-dev-html-child.js:135:3
- processChild.js:155 execFunction
[blog]/[jest-worker]/build/workers/processChild.js:155:17
- processChild.js:139 execHelper
[blog]/[jest-worker]/build/workers/processChild.js:139:5
- processChild.js:143 execMethod
[blog]/[jest-worker]/build/workers/processChild.js:143:5
- processChild.js:64 process.<anonymous>
[blog]/[jest-worker]/build/workers/processChild.js:64:7
- node:events:369 process.emit
node:events:369:20
- source-map-support.js:495 process.emit
[blog]/[source-map-support]/source-map-support.js:495:21
src/components/Form.js
import React from "react";
import { FormattedMessage, useIntl } from "gatsby-plugin-intl";
const Form = () => {
const intl = useIntl(); // hook; pass in object that contains id and default message to ensure that
return (
<div>
<label for="email">
<FormattedMessage id="email_label" defaultMessage="Email address" /> {/* pass id and defaultMessage down as props to render new string inside of a react fragment child element */}
</label>
<input
type="email"
id="email"
placeholder={intl.formattedMessage({
id: "email_input",
defaultMessage: "Email address",
})}
/>
<label for="Password">
<FormattedMessage id="password_label" defaultMessage="Password" />
</label>
<input type="password" id="password" />
<button type="submit" onSubmit={this.handleSubmit}>
<FormattedMessage id="submit_button" defaultMessage="submit" />
</button>
</div>
);
};
export default Form;
src/intl/en.json
{
"email_label": "Email address",
"password_label": "Password",
"submit_button": "submit"
}
src/intl/zh-TW.json
{
"email_label": "電郵地址",
"password_label": "密碼",
"submit_button": "提交"
}
src/pages/contact.js
import React from 'react'
import styled from 'styled-components'
import Navbar from '../components/Navbar.js'
import Form from '../components/Form.js'
const Body = styled.body`
margin-left: 6%;
`
export default function Contact() {
return (
<div>
<Navbar/>
<Body>
<Form/>
</Body>
</div>
)
}
Errors while building static HTML files (the build-time React SSR process) generally happen for one of the following reasons:
Some of your code references “browser globals” like window or document that aren’t available in Node.js. If this is your problem you should see an error above like “window is not defined”. To fix this, find the offending code and either a) check before calling the code if window is defined so the code doesn’t run while Gatsby is building (see code sample below) or b) if the code is in the render function of a React.js component, move that code into a componentDidMount lifecycle or into a useEffect hook, which ensures the code doesn’t run unless it’s in the browser.
Check that each of your JS files listed in your pages directory (and any sub-directories) are exporting either a React component or string. Gatsby treats any JS file listed under the pages dir as a page component, so it must have a default export that’s a component or string.
You mix up import and require calls in the same file. This might lead to “WebpackError: Invariant Violation: Minified React error #130” since webpack 4 is stricter than v3. The solution is to only use import and this also extends to gatsby-ssr and gatsby-browser files.
Your app doesn’t correctly hydrate in the client, which results in gatsby develop and gatsby build being inconsistent. It’s possible that a change in a file like gatsby-ssr or gatsby-browser has a structure that is not reflected in the other file, meaning that there is a mismatch between client and server output.
Some other reason :-) #1 is the most common reason building static files fail. If it’s another reason, you have to be a bit more creative in figuring out the problem.
How to check if window is defined
import * as React from "react"
// Check if window is defined (so if in the browser or in node.js).
const isBrowser = typeof window !== "undefined"
export default function MyComponent() {
let loggedIn = false
if (isBrowser) {
window.localstorage.getItem("isLoggedIn") === "true"
}
return <div>Am I logged in? {loggedIn}</div>
}

Cannot Define Type on 'this.$attrs' on Vue.js Component of Quasar App

I really need help with this and have been stuck for days. (It's also my first question so don't roast me.)
I've been trying to build my Quasar app, but all my TypeScript errors scream about Unsafe (assignment |member access | call) on an 'any' value I've narrowed it down to the fact that the this.$attrs that I use to pass the URL into my component has a type: any, but I have no idea how to set a type on the $attrs attribute and haven't found anything about how to do that. If someone could help that'd be amazing.
Basically, what my app is trying to do is grab the slug attribute, and based on that it'll look up the appropriate page in my props. My props are populated with posts from the Ghost Blog API.
Here's an example block of where my errors are:
import Vue from 'vue'
import { mapState } from 'vuex';
import GhostModule from '../store';
import { PostOrPage, PostsOrPages } from '#tryghost/content-api';
export interface WhyPageState {
page: PostOrPage;
slug: PostOrPage['slug'];
}
declare module 'vue/types/vue' {
interface Vue {
$attrs: {
slug: string
}
}
}
export default {
name: 'Page',
created() {
this.slug = this.$attrs.slug; // unsafe assignment of an any value
},
computed: mapState({
page(state: typeof GhostModule) {
const result: PostsOrPages = state?.GhostModule?.pages.filter(
page => page.slug === this.slug // Unsafe member access on any value, Unsafe member access .slug on an any value
);
return result[0]; // Unsafe return of an any typed value, Unsafe member access[0] on an any value
}
})
};
If anyone has a solution on how to define types to $attrs or another solution entirely on how to populate ghost page data dynamically, it'd be appreciated.

Cannot find module in VueJS Error using a JSON file

I was given a task in finding a way to dynamically import pages inside vuejs projects; built with a CLI tool the team I am in, is working on. I tried using functions as a string ("() => import(...)") and then eval that string and that did not work. Currenly I have used:
{
"routes": [
{
"name": "Login",
"path": "/login",
"component": "../../src/pages/auth/login/*"
},
{
"name": "Register",
"path": "/register",
"component": "#/pages/auth/register"
}
]
}
then I use a "driver" to then pass into our routes.ts file:
import * as dynamicRoutes from './routes.json';
const routes: any[] = [];
dynamicRoutes.default.routes.forEach((elem: any) => {
const component = async () => await require(elem.component).then((comp: any) => comp);
routes.push({
name: elem.name,
path: elem.path,
component,
});
});
export default routes;
Which im getting "cannnot find module" errors. Also when i log the output i get this when i check the component function: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.invokeGetter.
Is there a way that i can dynamically pass routes using this json file? (Because we are reading and writing to/from the json file to keep/update the routes)
You can use dynamic imports to lazy load Vue components, and it's even included by default when you install vue-router
const component = () => import(elem.component)
when you say dynamically import pages, I believe it is something related to lazy load the component dynamically ? If so, you need to have a HOC for all your route components which can basically use es6 import() (promise based) to fetch the component . You can also use webpack magic comments for the same(if you are using webpack).
hope it helps

React: How to add new value to props from imported file?

I am (very) new to React and have been given the task of adding some data to a component that's being brought in from another file. This file spits out some JSON and I want to access certain pieces of data from it, for example:
config.forms.enquiry.title
I am importing the file fine - no problems there. But I am not sure how to include config into my props.
I found a working example, in another file, and have copied what it does. My code is as such
Brings in file with JSON:
import { withSettings } from 'services/settingsFile';
Add config in render function:
render () {
const styles = getStyles(this.props, this.context, this.state);
const { config } = this.props;
// other stuff
Add to propTypes:
enquiryForm.propTypes = {
config: PropTypes.object.isRequired,
// other stuff
Add to compose:
export const enquiryForm = compose(
withSettings,
// other stuff
However, I get the error:
Failed context type: The context config is marked as required in
n, but its value is undefined.
And from here I am not sure what to do. I know it's a tough question, but I know very little about React and have been thrown in the deep end.
Would anyone know what/where I should be searching for to fix this?
If you can import it like,
import { withSettings } from 'services/settingsFile';
why dont you use it like,
const { config } = withSettings;
OK, so the issue was that there was no wrapping element setting config as am attribute.
I had to go up a level to where my component was being brought in and wrap:
<SettingsFile config={window.settingsFile}>
around:
<Component conf={config} />
Then, the component I was working on was able to read config.

react Error in ./src/App.js 'store' is not defined

attempting to follow this tutorial
https://thinkster.io/tutorials/setting-up-react-redux/introducing-react-redux
App.js file-
import React from 'react';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
appName: state.appName
});
class App extends React.Component {
render() {
const onClick = () => store.dispatch({ type: 'TOGGLE' });
return (
<div>
{ this.props.appName }
</div>
);
}
}
export default connect(mapStateToProps, () => ({}))(App);
according to the video, by importing the connect function and defining mapStateToProps we will get access to store. it does not work.
also, the code in the video is different from the code in the guide.
so at this point i'm not sure if i'm doing something incorrectly, or if this guide is just bad. can anyone suggest a better guide for learning react?
full error message-
Failed to compile.
Error in ./src/App.js
c:\Sites\react_frontend\django-frontend\src\App.js
14:11 warning 'onClick' is assigned a value but never used no-unused-vars
14:27 error 'store' is not defined no-undef
✖ 2 problems (1 error, 1 warning)
This tutorial is not very good - a lot of things are missing.
A previous step had the following line:
const store = createStore(reducer);
I am also using Thinkster and the line below worked for me. I found that there is no need to provide the empty object at all.
export default connect(mapStateToProps)(App);
I agree with you that the react redux tutorial is quite poorly done. I found that the Backend Tutorials(Express and mongo) was a lot more cohesive than the front end. The tutorials linked together well and each one started where the other left off.

Categories