I'm trying to figure out how I can use some existing functions in my React app.
There is a javascript file called Foo.js and and associated typing Foo.d.ts
Foo.js has many functions and at the top I see
//I don't understand what the below lines re doing
var A = A || {};
A.B= A.B|| {};
A.B.C= A.B.C|| {};
A.B.C.Foo= A.B.C.Foo|| {};
(function ()
//here there are many functions
)();
Then in the d.ts file
declare namespace A.B.C.Foo
{
//here there are exported functions
}
Now I have my react components in another folder, and I wish to use the functions defined in the Foo.js file. How do I use them?
I've tried adding the below to my component
///<reference path="../../../typings/#Test/Foo.d.ts" />
My component looks like below
const Component: React.FunctionComponent<IProps> = (props:Iprops) =>
{
//I'm trying to use the function here.
}
What do I need to do to import the functions? I have no idea how to proceed.
Edit: One more piece of information:
In the html file I have a reference to Foo.js added in a script tag
" />
Edit: So I was able to get this to work. The accepted answer pointed me in the right direction with using global. Looks like Foo.js was already declared in a manner that the functions were available in React already.
On the react side, adding the below reference path worked. To get type checking what I was missing was that that the .d.ts file exports a namespace so I had to prefix all functions with that namespace like so: A.B.C.Foo.FunctionName()
///
The best way to expose functions to the other modules of your application is when a module provides a export statement:
// Foo.js
export const mySpecialFunction = () => {}
// otherFile.js
import { mySpecialFunction } from './Foo.js';
mySpecialFunction();
Otherwise, you will need to rely on window (considering your code runs on browser) to expose you function globally by import Foo.js somewhere (preferably on your app's entrypoint) and using the function:
// Foo.js
window.mySpecialFunction = () => {}
// index.js (entrypoint of your app)
import './Foo.js';
// otherFile.js
mySpecialFunction();
But about Typescript, you can't just define a namespace. It must match with a path alias you define on tsconfig.js which points to Foo.js.
The fact is that isn't clear what you are aiming.
Edit:
How would I do this "typescript as a global module attached to window"?
You could follow this documentation https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-d-ts.html. It will help you to define a global function or namespace.
Disclaimer: In a general way, do not rely on global modules as they decrease the code readibility.
Normally for a well formed library with exported functions you should add:
import FooFunctions from "A.B.C.Foo"
in your component, and then call the functions with FooFunction.{theFunctionYouWantToCall}(). You can also use this format of import:
import {theMatchingFunctionYouWantToCall} from "A.B.C.Foo"
Note: that's the normal scenario if you imported the library from npm, and it is in your node_modules. If you created this library or created the missing .d.ts file for it, make sure that Typescript is finding your .d.ts first.
Related
I want to import .ts, .tsx, .js, and .jsx files into a react component and render them within a PrismJS highlighting block. For example, let's say I have a TypeScript file with functionA in it that I want to highlight in my actual website:
functionA.ts:
export function functionA() {
console.log("I am function A!");
}
I want to include this in a different component. The problem is, when I import it, I am obviously importing the webpack module version of it. My weak attempt at trying to get my function render in a react component looks like this:
MyComponent.tsx:
import * as React from "react"
import { functionA } from "./functionA"
export function MyComponent() {
return (
<>
<h1>Here is your code block:</h1>
<pre>
<code>
{functionA.toString()}
</code>
</pre>
</>
)
}
and what will actually render on the page where the code block is will look something like this:
Here is your code block:
WEBPACK__IMPORT.functionA() {
console.log("I am function A!")
}
I can't exactly remember what the .toString() function output looked like, but the point is it is NOT just the contents of the file how it appears in a code edit for example - it has been modulized by WebPack.
So, in a Gatsby project, how can i get these various code snippets to be imported directly as a string, purely as they are written, without WebPack enacting its import stuff on it? Is there a plugin or some way to tell Webpack to use the imported file as its asset/source module type? I know for MD or MDX files there is the gatsby-remark-embed-snippet, but I am building a component based HTML page and can't use MD or MDX files!
It's very late, and perhaps I just can't see the forest from the trees, I know there must be a way to do this...
You need to require the file using webpack's raw-loader, i.e:
const functionA = require("!!raw-loader!./functionA");
This works for create-react-app, as in the solution discussed here, and this works for Gatsby as well!
After using require on such a file, the file contents can be rendered in the component as:
<pre>{functionA.default.toString()}</pre>
It's then up to you to add syntax highlighting using a tool like prism or similar.
Note this solution will only work as long as Gatsby V3 continues to use WebPack v4, as raw-loader is deprecated in WebPack v5 and will be phased out for asset/source type modules.
I would like to store some variables inside a module (export) to be used as constants though out my react app. I would like to avoid context because there is no need for components to re-render and also I need those constants to be used outside my react components.
Where should I do it (where to import it), in order to prevent garbage collection?
One idea I have is to import and re-export it on top of my root component.
EDIT:
To be more precise, there will be a component that will set the constant once (mutate the variable), so that other components or files can access it.
So, what you will need is some sort of setter/getter pattern. Though I mostly don't recommend it unless you know what you are doing, because React won't re-render if the variable changes and because of that you need to be sure the variable is set before it is used.
You should have something like the example below in order for it to work the way you want. You can find an example of it working on this Codesandbox.
export let MY_VARIABLE = "";
export const setMyVariable = value => (MY_VARIABLE = value);
PS: I've added some console.log to the code in order for you to see how the import/get/set behaves.
After digging more into this I found that es6 module spec states:
When import your module it gets loaded => parsed => evaluated and cached (singleton). It also says that when you import modules its value is passed by reference (aka assignment). I didn't find anything mentioning when or how es6 modules are unloaded from that cache.
So that means, when you import a module once, it is there for as long as the program is running, and all modules access its values directly.
reference
https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
https://medium.com/#mivanichok/understanding-es6-modules-in-depth-article-b49612926e39
You can create an config.js inside src folder and write the your constant variable like
//config.js
module.exports = {
CONST_VAR : 'const value',
}
import config.js in your component and use it
If you have a parent component file that already imports React, why does any of its rendered children files also need to import React? Is it just a safety measure in case those children are ever rendered somewhere else where React has not been imported yet?
In nodejs each file is a module, that has its own scope of variables. When you import variable into file (React for example) you add this variable to the module scope, but not to the global scope.
In case of webpack you can use providePlugin to easily make React variable global:
new webpack.ProvidePlugin({
React: 'react' // ReactJS module name in node_modules folder
})
After that you are able to skip importing React variable in all of your modules. Webpack will do it itself where needed.
If you use JSX and babel, you have to import React in every file because babel-preset-react will transpile your JSX code into React.createElement() calls, so this code
const Foo = () => <h1>Test</h1>;
will be
var Foo = function Foo() {
return React.createElement(
"h1",
null,
"Test"
);
};
DEMO: Babel REPL
This is the reason why React should be accessible in the proper scope and one way to do it, is to import React in a file.
The root of the question is one of dependency management -- how do I, the author, describe and obtain external dependencies I need in my "thing" (application/component/module) for it to do its job?
JavaScript benefits (or suffers) from having a global namespace in which dependencies can be injected. While this can often simplify dependency management in the short term (e.g. you can ignore it and expect everything you need to be available in the global namespace), it can often cause issues as an application grows and changes. For example, given an application with multiple contributors, one might decide to change a part of the application to no longer use a particular dependency and therefore remove it. If another part of the application needed it but that dependency wasn't formally declared anywhere, it could be easily missed.
To do dependency management well, each discrete "thing" should describe its dependencies independent of any other "thing" such that it can be safely used in any given context. This ensures that each "thing" has exactly what it needs no matter how it is used and what its "parent" (the code importing the "thing") has.
An alternative to this approach is dependency injection. In this model, the "thing" provides an interface for passing the dependencies into the "thing" from the consumer. There are flexibility and testability advantages to this which are out of scope of your question. :)
// export a function that expects the React and PropTypes
// dependencies to be injected as parameters and returns
// the component rather than importing the dependencies
// and exporting the component
export default (React, PropTypes) => {
return class extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired
}
render () {
return (
<div />
);
}
};
};
All of that to say, it is somewhat a "safety measure" to have each "thing" import its own dependencies. It lets you safely refactor your code without the cognitive overhead of remembering what is providing those dependencies.
The reason is to avoid unnecessary compiled a JavaScript code that you don't have to compile jsx. For example, you have a file that have a function to do adding, or whatever function that doesn't need to compile jsx, then you don't put import React from 'react' on the top of that file. This will save you compile time.
I want to augment the IHttpPromise<T> inside the declared angular namespace (from "#types/angular"). So that I'll be able to write $http.then(...).myCustomMethod(...).
I was able to augment interfaces in the global context easily by just writing:
interface JQuery {
$custom: string;
}
For interfaces inside namespaces, the same doesn't work obviously. Tried several things like declaring the angular namespace again and putting the augmented interface inside, but that and other numerous variations didn't work.
Note that I'm not using (external) modules.
Found the solution. Even if you're not using modules, you'll have to do the following in a separate declaration file:
import { IHttpPromise } from '#types/angular';
declare module '#types/angular' {
interface IHttpPromise<T> {
myCustomMethod(): something;
}
}
The import statement's content itself is not important. We have to do an import to make TS treat this file as a module for this to work. So this would've also worked:
import * as angular from '#types/angular';
This is documented at the end here.
Context
I have an ES6/react-js file called ExternalComponent.react.jsx, with the following structure:
import React from 'react'
import Swipeable from 'react-swipeable'
const ExternalComponent = React.createClass({
//...
})
export default ExternalComponent
I have used browserify/babel to compile this file into its ES5 version (the new ES5 file is called my-external-component-in-ES5.js) using the following command:
browserify -t babelify --presets react MyExternalComponent.react.jsx -o my-external-component-in-ES5.js
The output of this file is quite large (>20,000 lines of javascript); however, it appears to wrap ExternalComponent in a large IIFE (might be wrong about this).
Problem
My goal is to access the class ExternalComponent from a pure ES5 context (in my development environment, I am unable to use ES6). I'm assuming this is going to involve one of the following:
Within ExternalComponent.react.jsx, somehow add ExternalComponent to the global namespace, so that when it compiles to ES5 I can just refer to ExternalComponent by its name.
Somehow access the ExternalComponent class which is buried in the massive my-external-component-in-ES5.js using ES5 syntax.
I'm not sure how to do either (1) or (2).
NOTE: In case anyone is wondering why I want to do this, it's because I'm trying to use the ExternalComponent within ClojureScript (which only has ES5 javascript interop; hence I have to figure out how to access ExternalComponent using only ES5 syntax!).
When you compile an ES6 module to ES5 with Browserify it converts the import syntax into CommonJS calls.
This:
import foo from './foobar';
Becomes:
var foo = require('./foobar');
You can access and use your class exactly as you would expect from your ES5 file, there's no need to clobber the global namespace. Just use the CommonJS functions.
var ExternalComponent = require('./my-external-component-in-ES5');
ReactDOM.render
<ExternalComponent />,
document.getElementById('app')
);
If you're trying to do this from ClojureScript, then I suggest creating a standalone browserify build that exposes your modules with global variables. You can use a tool like browserify-umdify.
// external-component.js
export default ExternalComponent
This will be compiled to a Javascript file that exposes externalComponent as a global variable on the window object.
Compile it to somewhere like resources/public/js/compiled/bundled-deps.js, then add it to your index.html with a script tag (above your cljs build).
Then you'll be able to reference your JS modules through the JS namespace.
(def external-component js/ExternalComponent)