I've been having an issue with importing a react class into a container
My file organization is as follows:
├── components
│ ├── Header
│ │ ├── Header.js
│ │ └── index.js
│ └── index.js
├── containers
│ └── HeaderContainer.js
└── index.js
where components/Header/Header.js exports with
export default class Header extends Component {}
components/Header/index.js is
import Header from './Header';
import './Header.scss';
export default Header;
and components/index.js is
export Header from './Header';
and containers/HeaderContainer.js is trying to import with
import { Header } from '../components';
However, this doesn't work, Header is undefined.
If I use import * as components from '../components';,
Header is listed, but using components.Header is again undefined.
However, it works perfectly if I instead do
import Header from '../components/Header';
Can anyone explain why the first two methods don't seem to be working? I've done it before this way, and I cannot figure out what I may have changed (admittedly, part of the reason I'm asking is just to type it out a new way and seeing if it helps)
Additionally, I've been able to use
import { Header } from './components';
from an index file in the main directory, which worked perfectly. The issue seems to somehow be with import { Header } from '../components' only
You probably have a cyclical dependency issue. Consider:
components/index.js starts loading.
It sees it needs containers/HeaderContainer.js, so it suspends.
containers/HeaderContainer.js starts loading.
It sees it needs import { Header } from '../components';, so it suspends.
components/index.js is already loading from step 1, so this step is a no-op.
containers/HeaderContainer.js starts running again.
Since components/index.js is still loading, the imported Header is pointing to a variable that hasn't been initialized yet, like if you did
console.log(Header);
let Header = ...
Babel's behavior in this situation is to make Header undefined. In a real native ES6 module environment, it would throw an exception because Header wasn't initialized yet.
There are two main options to fix this. Either one should help:
Import ../components/Header directly to avoid the already-loading components/index.js.
Reorder your imports so Header is already initialized in components. e.g.
export { HeaderContainer } from './containers/HeaderContainer';
export { Header } from './Header';
to
export { Header } from './Header';
export { HeaderContainer } from './containers/HeaderContainer';
There are two types of export: named and default.
Named export should be consumed by using construct like:
import { Header } from '../components';
Default on the other hand should be consumed like:
import Header from '../components';
You can read more here;
Because the only thing you can omit from the path is index.js
So if you have
import Header from '../components/Header';
It is the same as
import Header from '../components/Header/index.js';
But if you type
import { Header } from '../components';
Interpreter expects that in the file ../components/index.js or it could be just ../components.js if components is a file and not a folder, it will find a named export
export const Header = () => {...}
More about exports
Related
I'm building a plugin for storyblok with vue. I decided it would be easier to add state management to the plugin rather than an echo chamber of $emit() all the way back up the component tree. I installed Vuex into my plugin and went to add it to the main.js file as instructed in a Vuex tutorial however my main.js file isn't set up like a regular Vue app's main.js file.
The tutorial tells us to do this
import Vue from 'vue'
import App from './App.vue'
import store from './store' //our Vuex store
Vue.config.productionTip = false
new Vue({
store, //passing in our store
render: h =>(App)
}).$mount('#app')
However my main.js file due to being a storyblok plugin looks like this
import Plugin from './Plugin.vue'
import store from './store' //I have no clue where to put this in the code below :(
if (process.env.NODE_ENV == 'development') {
window.Fieldtype = Plugin
let customComp = window.Storyblok.vue.extend(window.Fieldtype);
window.Storyblok.vue.component('custom-plugin', customComp);
window.StoryblokPluginRegistered = true;
} else {
let init = Plugin.methods.initWith()
window.storyblok.field_types[init.plugin] = Plugin
}
As you can see the setup is totally different as it's geared towards injecting a Vue component into storyblok as a plugin rather than setting up a new Vue app. Does anybody know what I should do here?
I solve that issue adding in the main.js this line: window.Storyblok.vue.$store = store;
Then in your plugin when you call the store use the same.
For example:
window.Storyblok.vue.$store.commit()
So I am still learning StoryBlok and their plugin development, so please yell at me if this is wrong.
I was able to solve this like so.
Just for some context this is my general folder structure.
.
└── my-app/
├── node_module/
│ └── ...
├── public/
│ └── index.html
└── src/
├── store/
│ ├── state.js
│ └── config.js
├── main.js
└── Plugin.vue
main.js
import Vuex from 'vuex'
window.Storyblok.vue.use(Vuex); // This has to come before the Plugin import
import Plugin from './Plugin.vue'
if (process.env.NODE_ENV == 'development') {
window.Fieldtype = Plugin
let customComp = window.Storyblok.vue.extend(window.Fieldtype);
window.Storyblok.vue.extend(window.Fieldtype);
window.Storyblok.vue.component('custom-plugin', customComp);
window.StoryblokPluginRegistered = true;
} else {
let init = Plugin.methods.initWith()
window.storyblok.field_types[init.plugin] = Plugin
}
Plugin.vue
import Vuex from 'vuex';
import State from './store/state';
const store = new Vuex.Store(State);
export default {
name: 'my-vue-plugin',
mixins: [window.Storyblok.plugin],
store: store,
components: {},
data() {
return {}
},
};
state.js
import Config from './config';
export default {
modules: {
Config
},
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
}
};
Then anywhere in your nested components or whatever you can call the increment mutation like so this.$store.commit('increment');
I'm trying to mock my import with animations, but I keep getting
● Test suite failed to run
C:\work\portfolio\node_modules\gsap\TweenMax.js:13
import TweenLite, { TweenPlugin, Ease, Power0, Power1, Power2, Power3, Power4, Linear } from "./TweenLite.js";
^^^^^^^^^
SyntaxError: Unexpected identifier
The error is from one of the imports from my file.
App.js
const App = () => (
<ChronologyGraph
width="700"
height="800"
nodeSize={10}
milestones={milestones.reverse()}
columns={nodeTypes}
/>
);
export default App;
inside of ChronologyGraph I import my component ProjectNode which imports another file I made animation.js and inside of animation.js I import
import { TimelineMax, Power0 } from "gsap/TweenMax";
import { TweenMax } from "gsap/TweenMaxBase";
Which are causing the error above, I want to either mock this gsap library or just my animation.js
App.test.js
import React from "react";
import { shallow } from 'enzyme';
import App from "./App";
fit("renders without crashing", () => {
jest.mock('../animation.js');
jest.mock('gsap/TweenMaxBase');
jest.mock('gsap/TweenMax');
const wrapper = shallow(<App />);
});
And here are all the mocks I've tried without any success
Coming late to this, but sharing because the solution is very simple. (I've already answered here as well)
If you read Jest documentation you can simply mock GSAP creating a file in __mocks__ directory.
Mocking TweenMax
Let's say you are importing TweenMax and you want to use to method:
import { TweenMax } from "gsap/TweenMax";
Add two files into the mocks directory. TweenLite can be empty.
.
└── __mocks__
└── gsap
└── TweenMax.js
└── TweenLite.js
module.exports = {
TweenMax: class{
static to(selector, time, options) {
return jest.fn();
}
}
}
You've successfully mock your TweenMax.to method.
Mocking TimelineMax
Because TimelineMax works on instances of a class the mock should be done this way:
import { TimelineMax } from "gsap/TimelineMax";
Again, add two files into the mocks directory. TweenLite can be empty.
.
└── __mocks__
└── gsap
└── TweenLite.js
└── TimelineMax.js
module.exports = {
TimelineMax: class {
constructor(){
this.to = jest.fn().mockReturnThis();
}
}
};
Use mockReturnThis() to be able to chain methods.
You could try using the UMD version instead, like:
import TweenLite from "gsap/umd/TweenLite"
please update your jest config:
"transform": {
"\\.js$": "<rootDir>/node_modules/babel-jest"
}
and install babel-jest.
Further if your issue wouldn't get resolved then share your jest config and we don't usually mock imports.
Update 2 - Add minimal 'working' example showing the issue
I trimmed down the project as far as I could while still showing the issue, to allow people to try out ideas/debug if they're interested
github:store_import_test
The error happens in: request.js
Note: I'm aware the bounty is about to expire, but I'll re-enable it if that happens. I do appreciate all ideas/help put out so far!
End update 2
Update 1 - purpose description:
I want to access a value from the store (that can change overtime) in a 'utility function'. According to the redux docs subscribe is a valid option.
End update
I'm trying to import my redux-store outside of a component (in request.js , see below) similar to: What is the best way to access redux store outside a react component?
However, all those solutions (including https://github.com/reactjs/redux/issues/776) don't work because my request.js tries to import the store before createStore() is called in store.js resulting in store being undefined.
My directory structure looks like this
.
├── app
│ ├── api
│ ├── network
│ | └── request.js
│ ├── app.js
│ ├── store.js
├── index.android.js
├── index.ios.js
The index.android/ios.js are the entry points and just load app.js
index.android/ios.js
import App from './app/app'
app.js
import store from './store'
class App extends React.Component {
render() {
return (
<Provider store={store}>
<RootStackNavigation />
</Provider>
)
}
}
store.js
...
const store = createStore(reducer, initialState, middleware())
export default store
request.js
import store from '../../store'
store.subscribe(listener)
...
const someFunc(){}
export default someFunc
My thoughts / what I tried / where I get lost
Note: the store import path in request.js is valid, double checked
Note2: the store is usable in app.js and the remainder of the program
I would think that the import store from '../../store' in request.js would trigger the const store = createStore(reducer, initialState, middleware()) line but apparently it seems it does.
attempt 1
I tried to also export the store as such:
export const store = createStore(reducer, initialState, middleware())
and imported it in request.js as:
import {store} from '../../store
thinking maybe the 'clever' default loading does some magic I don't know/understand. Same error, undefined
attempt 2 add getStore() to store.js
let store = null
store = createStore(reducer, initialState, middleware())
export function getStore() {
if (store == null) {
store = createStore(reducer, initialState, middleware())
}
return store
}
export default store
Doesn't work, the arguments to createStore have not been initialised yet.
I must be misunderstanding the loading process or mixing it with that of python, but what's preventing this from working? Others seem to be using the same solution (see the above mentioned posts) successfully.
Found the problem. I was right - it’s a cycle. In store.js you require reducers/index.js then navigation.js then ../navigators/RootNavigationConfiguration which requires Home.js which requires /api/network which requires request.js and it requires store which at this point is not initialized. Try moving the store.subscribe(listener) along with the listener function to store.js right before you export it. Don’t forget to remove import store.js from request.js
I feel that you want to make antipattern. Your needcase is whispering to my ears "saga", but let's help you as it is.
I want to give you closed issue from github, but I want to make it clear where "they" are getting store - import {store} from "store"; is exported const store = configureStore(); and configure store (to avoid missunderstanding) is returning redux.createStore(...).
After you know what I wrote - you can understand what "they" are writing there and there.
Did you write "../../store" in request.js, but your directory structure says it should be "../store".
You can simply create your store in the App Component to reduce complications, and pass it through the to sub-components.
let store = createStore(reducers);
export class App extends Component {
constructor() {
super();
this.state = store.getState();
this.syncState = this.syncState.bind(this);
}
syncState() {
this.setState(store.getState());
}
componentDidMount() {
this.setState(store.getState());
const unsubscribe = store.subscribe(this.syncState);
}
render() {
return (
<Provider store={store}>
<AppNavigator/>
</Provider>
)
}
}
Your sub-components then need to import connect from react-redux.
import {connect} from 'react-redux';
export class SubComponent extends Component {
//Implementation
//this.props.myLocalCopy - gives you the value
//this.props.someTask(data) - Updates the store and updates your props.
}
function mapStateToProps(state) {
return {
myLocalCopy: state.someStoreProperty
};
}
function mapDispatchToProps(dispatch) {
return {
someTask: (data) => {
dispatch(actionCreators.someTask(data));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SubComponent)
This will help you to synchronize your app state with store throughout the workflow.
Hope that helps! :)
I have been trying to get my head around React Native as I recently took the decision to switch to it from Cordova.
I have been trying to understand how container and component files should be properly structured inside src in order to correctly build.
To this end I have been attempting to run the initial index.android.js code out of a new file "app.js" which I have created in a folder I called js found in the original /src/main folder.
This is the index file code
/*Both index files index.ios.js and index.android.js MUST be indentical*/
var React= require('react-native');
var { AppRegistry } = React;
var App = require('./android/app/src/main/js/app.js')
AppRegistry.registerComponent('LearnD', () => LearnD);
And the app.js file can be found at this gist here.
I have been then receiving the following error:
Any help will be more than appreciated.
Thanks in advance,
Jake.
A typical setup for a React Native application looks something like:
└── YourApp
├── android # Native Android files, no .js here
├── index.android.js # Android entry point, must exist
├── index.ios.js # iOS entry point, must exist
├── ios # Native iOS files, no .js here
└── src # All your .js sources
├── components # All your .js sources
└── main.js # Your shared entry point
Your src/main.js can then export a single, shared entry point component for both platforms and uses other components inside the src/ directory:
// src/main.js
import React, { Component } from 'react';
import { View } from 'react-native';
import OtherComponent from './components/other-component.js'
// note export default
export default class Main extends Component {
render() {
return (
<View>
<OtherComponent />
</View>
);
}
}
And your index.ios.js and index.android.js components can import and register the main component as the application root component:
// index.ios.js and index.android.js
// both must exist, but they can be identical
import { AppRegistry } from 'react-native';
import Main from './src/main';
AppRegistry.registerComponent('YourApp', () => Main);
Within the src directory, you can then structure your app code in any way you best see fit, e.g. src/components and src/containers - entirely up to you!
Your file is trying to export App on line 48, which does not exist. You already have export default class LearnD on line 10, so omit line 48 and that should help
I just started with React.js and I am unable to import component.
I have this structure as followed by this tutorial (YouTube link) :
-- src
----| index.html
----| app
------| index.js
------| components
--------| MyCompontent.js
This is my index.js:
import React from 'react';
import { render } from 'react-dom';
import { MyCompontent } from "./components/MyCompontent";
class App extends React.Component {
render() {
return (
<div>
<h1>Foo</h1>
<MyCompontent/>
</div>
);
}
}
render(<App />, window.document.getElementById('app'));
This is MyComponent.js:
import React from "react";
export class MyCompontent extends React.Component {
render(){
return(
<div>MyCompontent</div>
);
}
}
I am using this webpack file (GitHub link).
However, when I run this, my module fails to load.
I get this error in the browser console:
Error: Cannot find module "./components/MyCompontent"
[WDS] Hot Module Replacement enabled. bundle.js:631:11
[WDS] Errors while compiling. bundle.js:631:11
./src/app/index.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./components/MyCompontent in /home/kuno/code/react-hoteli/src/app
resolve file
/home/kuno/code/react-hoteli/src/app/components/MyCompontent doesn't exist
/home/kuno/code/react-hoteli/src/app/components/MyCompontent.webpack.js doesn't exist
/home/kuno/code/react-hoteli/src/app/components/MyCompontent.web.js doesn't exist
/home/kuno/code/react-hoteli/src/app/components/MyCompontent.js doesn't exist
/home/kuno/code/react-hoteli/src/app/components/MyCompontent.json doesn't exist
resolve directory
/home/kuno/code/react-hoteli/src/app/components/MyCompontent/package.json doesn't exist (directory description file)
/home/kuno/code/react-hoteli/src/app/components/MyCompontent doesn't exist (directory default file)
[/home/kuno/code/react-hoteli/src/app/components/MyCompontent]
[/home/kuno/code/react-hoteli/src/app/components/MyCompontent.webpack.js]
[/home/kuno/code/react-hoteli/src/app/components/MyCompontent.web.js]
[/home/kuno/code/react-hoteli/src/app/components/MyCompontent.js]
[/home/kuno/code/react-hoteli/src/app/components/MyCompontent.json]
# ./src/app/index.js 11:20-56 bundle.js:669:5
Can't figure out what went wrong here.
For anyone coming here without a typo, and is using Webpack, be sure to check for a clause like this:
resolve: {
extensions: [".jsx", ".js"]
},
in your webpack.config.js.
This tells your transpiler to resolve statements like:
import Setup from './components/Setup'
to
import Setup from './components/Setup.jsx'
This way you don't need the extension.
You have a typo in your import. You're requesting MyCompontent. Change to:
import MyComponent from "./components/MyComponent";
And all typos as well.
You can try to import MyCompontent from "./components/MyCompontent.js"
like this
import MyCompontent from "./components/MyCompontent.js";
You have written that the filename is MyComponent.js.
Thus, your import should look like
import { MyCompontent } from './components/MyComponent.js'
The problem for me was that import line was not generated correctly. I have this scenario:
--src
----elements
-----myCustomText.tsx
this is myCustomText.tsx file:
export interface Props {
text: string;
}
const MyCustomText = ({ text }: Props) => (
<Text>{text}</Text>
);
export default MyCustomText
And the generated import was this:
import MyCustomText from '../../elements/MyCustomText';
and I changed it to this:
import MyCustomText from '../../elements/myCustomText'
I don't know why the generated import line was generated automatically wrong.
I found myself here without a typo and without a webpack issue.
The solution for me was to restart the typescript server via VS Code.
I just had this issue, no type or webpack config issues.
What fixed it was changing the import from relative to the app root directory to relative to the file:
import MyComponent from "src/components/MyComponent";
to
import MyComponent from "../components/MyComponent";
If you're getting this from Visual Studio Code auto-importing via the shortest route, you can change it so it imports relatively. By going here:
menu File → Preferences → Settings → User Settings,
"typescript.preferences.importModuleSpecifier": "relative"
export 'Component' (imported as 'Component') was not found in 'react'
if you find your self stuck with this error simply go to mini-create-react-context, and go to cjs, and go to index.js and add "React" example: you will find this (Component) solution (React.Component) only if you extends to React.Component in you pages
Note: I have only used this on VS Code