It's the same question as here, but I don't understand the answer. So, my question is about the answer.
The answer is
Your react.js file/module isn't exposing the variables React and
ReactDOM you instantiate. In node, you make these methods public by
modifying the module.exports object like so:
module.exports = { React: React, ReactDOM: ReactDOM }
Finally, my question is: Where do "you make these methods public"?
You are making these methods public by defining them on the module.exports property. For instance, say you have a file reactExports.js with
var iWontGetThere = 'I am in this module only';
module.exports = { React: React, ReactDOM: ReactDOM }
now in another file, you can require these methods and use them, like so:
var React = require('reactExports').React;
var SweetComponent = React.createClass({
render: function() {
return (
<div>React is cool and I can use it in here!</div>
);
}
});
module.exports = SweetComponent;
Now imagine you want to render SweetComponent in another component. If I hadn't written module.exports = SweetComponent, requiring this module in another component would have no effect as all as you would be importing an empty object {}.
Let's say I tried to console.log(React.iWontGetThere); What would happen? I would get a reference error, as it was not exported with the contents of reactExports.js-it only exists in that module, but it is not exposed.
This is a contrived example, but the interesting story here is the ability to bring in encapsulated modules. I suggest reading more about node modules here
and checking out this answer as well.
and making some examples to get the hang of it.
tl;dr: defining variables and not having a module.exports = some_value statement then requiring that same file will default to an empty object.
Related
So I sort of found a fix for this, but I want to understand what's happening and why.
The background is that I'm testing some classes, each class has a field for the result of a hook for reasons that aren't relevant here. All that happens with the hook in the constructor of each class is that it gets assigned to the member variable.
In order to test each of these classes, I had originally mocked out a few things so that the hook behaved just like a normal function, and that worked fine. But then I discovered the renderHook feature of react testing library and figured it was probably better to use a mature tool instead.
The hook depends on a react context, so I followed the instructions and created this helper function to get my hook
import { mockContext } from './mockContext';
import { renderHook } from '#testing-library/react';
import { OAuthRequests } from '../lib/core/models/auth/OAuthRequests';
import { useOAuth } from '../lib/core/hooks/useOAuth';
import { ClientConfig } from '../lib/core/models/auth/RedditConfig';
export const mockClient: ClientConfig = { clientId: '', clientSecret: '' };
export const mockOAuth = () => {
const wrapper = mockContext();
const {
result: { current: requests },
} = renderHook<OAuthRequests, ClientConfig>(() => useOAuth(mockClient), { wrapper });
return requests;
};
The problem came when I refactored these test suites, all of which were passing. ONLY those suites which were subclasses of one particular superclass were failing. The message with the failure was
TypeError: Class extends value undefined is not a constructor or null
mockOAuth works just fine in every test suite except the ones that extend a certain abstract superclass. I get no runtime errors in my actual app using any of these classes, and like I said, before refactoring to use renderHook it was also all working fine with passing tests. Also, the error seemed to happen at the import stage, not at construction of any objects.
So, after a lot of debugging and trial and error, I discovered that if I placed my import of mockOAuth at the top of the file above the class imports, it works, but if it's below them, it breaks.
It's great that I have a working solution, but I really want to understand why this is happening in case it is a canary for some very obscure bug in my code that won't pop up easily.
Also, I'm tagging the hooks testing library as well here, but I am using react testing library 13.4.0 and react 18.1.0.
I am working on documentation tool for Typescript library. The idea is to leverage parcel's watch mode to continuously build the library, and use the same in a pre-built documentation app.
For the same I need to load a module library (built in another project) dynamically via URL.
<script type="module">
const libraryModule = "http://localhost:8080/lib.module.js";
const promise = import(libraryModule);
promise.then(library => {
// do something with library
window.ComponentLibrary = library;
});
</script>
However, parcel replaces the above import with require and the load fails. Using System.import throws System is not defined error.
I tried to use dynamic-import-polyfill and then initialize it as under and the use as below:
dynamicImportPolyfill.initialize({
modulePath: 'http://localhost:13090', // Defaults to '.'
importFunctionName: '$$import' // Defaults to '__import__'
const promise = $$import(libPath);
});
This throws the following error:
TypeError: Failed to resolve module specifier "react/jsx-dev-runtime". Relative references must start with either "/", "./", or "../"
I have also tried using script type as text/javascript but doesn't work either.
Looking for guidance on the best way here to get the component library loaded?
Figured it out: yes, we can load a component library as a module dynamically.
The issue was that React UMD module is not a pure ES/Javascript module. Also, with React 17, JSX components are picked from react/jsx-runtime. So, first I had to convert the React UMD module into an ES module - it's just a thin wrapper. Similarly, added a wrapper for jsx-runtime. To make things work had to use importmaps which are currently not supported in all browsers - see caniuse.com to check latest support.
This completes your setup and now your library compiled as ES module will work just fine. Below is what I used to get working:
<script type="importmap">
{
"imports": {
"react/jsx-runtime": "/react-jsx-runtime.js",
"react": "/react-as-es-module.js"
}
}
</script>
<script type="module" src="/component-library.js"></script>
<script type="module">
import * as MyComponentLib from "/component-library.js";
window.ComponentLibrary = { ...MyComponentLib };
</script>
Code for react-jsx-runtime.js looks as under:
import * as React from 'react';
export const jsx = React.createElement;
export const jsxs = React.createElement;
Code for react-as-es-module.js goes as:
import 'https://unpkg.com/react#17.0.2/umd/react.production.min.js';
const {
Children,
Component,
Fragment,
// and all other exports
} = React || {};
export {
Children,
Component,
Fragment,
// and all other exports
}
export default React;
I compiled component-library.js using ParcelJS using the type: "module" in package.json file. I would detail this in blog post and demo Github repo soon.
Hope this helps.
I know the doubt is very basic but I haven't found an answer yet.
Here it goes:
Let's say I have a utils file that exports some function and I'm using that same utils function in many of my React components.
This is what I'm currently doing:
component1:
import { parseData } from '#/utils/parser';
component2:
import { parseData } from '#/utils/parser';
How can I avoid importing it on every file?
If you are using webpack there is a plugin called PluginProvider:
And maybe you can use it like this:
new webpack.ProvidePlugin({
parseData: path.resolve(path.join(__dirname, '#/utils/parser'),
});
I recently read about React Lazy and how it "loads" the components during run time when they are required to be rendered. I assume that "loading" here means fetching the component from the server and then rendering it.
So my question is, how does React manage this fetching of components? How does it know the exact path from where to fetch this component (given that our code will mention the relative path but fetching will require complete server path)? Does it depend on Webpack for this?
Let's look into the React code. React.lazy is defined as follows.
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
let lazyType = {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result.
_status: -1,
_result: null,
};
if (__DEV__) {
// ... additional code only in development mode
}
return lazyType;
}
As you can see, React.lazy requires a Promise which resolves to a module with a default export containing a React component (freely cited by React Docs). This also means that not React resolves the file, but import() does. import() works as documented in the MDN.
The async import() is a new function in ES6 which is not available in all browsers but can be polyfilled by Webpack and Babel/Typescript/others.
What you often see is code like the following, which automatically splits the imported file away by Webpack.
import(/* webpackChunkName: "xyz" */ './component/XYZ')
This creates a new javascript xyz.js next to your bundle script.
If you don't use Webpack, you need to create those files by yourself. Webpack just reduces the work required from you. So you don't absolutely depend on Webpack. This approach might look like the following:
// ./component/xyz.js
export default function() { return <div>Component</div> }
// ./main.js
const OtherComponent = React.lazy(() => import('./component/xyz.js'));
export default function() { return <div>Component</div> }
And the file structure:
| public
|---| main.js
|---| component
|---| --- | main.js
As you see, no webpack required. It just makes your life easier.
Their __tests__/TodoStore-test.js file:
https://github.com/facebook/flux/blob/master/examples/flux-todomvc/js/stores/tests/TodoStore-test.js
And their js/dispatcher/AppDispatcher.js file:
https://github.com/facebook/flux/blob/master/examples/flux-todomvc/js/dispatcher/AppDispatcher.js
In their case, the dispatcher is simply this:
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
And in their test's beforeEach it resets:
AppDispatcher = require('../../dispatcher/AppDispatcher');
Now, in my own project I'd like to use ES6's way of doing things. I'm not sure how, though. The differences between require and import elude me.
My dispatcher:
import { Dispatcher } from 'flux';
var dispatcher = new Dispatcher({
logLevel: 'ALL'
});
export default dispatcher;
Which–as a piece in the app–works fine in my app, but the require example above isn't having it.
The part of the test that needs to be converted to ES6's import:
beforeEach(function() {
AppDispatcher = require('./../src/dispatcher/dispatcher');
TodoStore = require('./../src/stores/TodoStore');
callback = AppDispatcher.register.mock.calls[0][0];
});
But, of course, this does not work:
beforeEach(function() {
AppDispatcher = import './../src/dispatcher/dispatcher';
// snip
});
Or:
import dispatcher from './../src/dispatcher/dispatcher';
beforeEach(function() {
AppDispatcher = new dispatcher();
// snip
});
I'm just doing by trial and error now and it's taking way too much time just to write a test.
My thought process:
They require whatever their dispatcher file outputs, which is always a new Dispatcher().
In my case, I'm creating a new Dispatcher and export that instance. Instead, I'd probably need to not instantiate it in the dispatcher file, but leave that up to the export, e.g.: export default new Dispatcher(), but that looks awful.
But no matter what I try I keep getting errors like:
TypeError: Cannot read property 'register' of undefined
Does anybody have a clue how to get this one up and running?
Much obliged!
The ES6 import Syntax is actually import defaultMember from 'module-name';
So in your case it would be
import AppDispatcher from './../src/dispatcher/dispatcher';
import TodoStore from './../src/stores/TodoStore';
For further info on ES6 import read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import