React Component not created until render method - javascript

I am using Styled Components but getting a problem trying to style a component of my own creation.
It is exported in the normal way from a separate npm package.
Package
FormComponents.js
export SubmitButton from './_submit-button';
_submit-button.js
export styled.button`somestyles`;
components.js
import * as Form from './form-components';
export { Form }
index.js
import * as Components from './components'
export const FormComponents = Components.Form;
Then in my actual component I want to use it I have:
import { FormComponents as Form } from 'packagename';
const Button = styled(Form.SubmitButton)...
and I'm receiving the error cannot read SubmitButton of undefined.
If however I put the const Button = ... in the render method, it finds everything fine. So I assume that the component isn't being instantiated until React kicks in.
It's possible with other elements however, for example if I change it to use:
const Button = styled(Link)
(Link from react-router-dom)
it finds it fine.
Am I exporting wrong?

Related

How to dynamically import an object exported from another file?

In my node_modules, I have a folder with an index.js which exports an object which contains a bunch of icon objects.
export { arrow1, arrow2, arrow3 .. }
I want to somehow dynamically import an icon object from another component. I am using Stencil.js, which is similar to React, and I need to use the icon string passed as a prop to dynamically import that particular icon object. How do I do that? The issue is that the import statement must be at the top of the page, but the prop is defined below.
Is there a way to import an exported object without using the import statement?
I tried with fetch() but it wasn't working. It kept returning status not ok.
I don't see point how importing all icons is a performance issue.
Here is small example, but it depends on what format are the imported icons.
import { IconNames } from '../icon/types';
#Component({
tag: 'custom-component'
})
export class CustomComponent {
#Prop() iconName: keyof IconNames;
render() {
const unicodeIcon = String.fromCharCode(parseInt(getIconHexCharCode(this.iconName), 16));
return (
<span>{unicodeIcon}</span>
)
}
}

Why does my ReactJS export statement not work?

I'm ultimately trying to dynamically create an object of imported components to export all at once. But in my simple example below, I can't even export an object with 1 component. Why is that?
// index.js
// Card is a standard ReactJS component, exported with export default Card
import Card from './Card';
let Components = {};
Components['Card'] = Card;
// this also doesn't work
// Components['Card'] = require('./Card').default
export default Components;
// Error message: "Attempted import error: 'Card' is not exported from './index.js'"
As the error says, you don't have a named export Cards. You have a default export, Components, which is an object with a property Cards.
If you want to have named export Cards then do
export {Cards};
Or if you don't even need the Components object, you can re-export the component directly with
export {default as Card} from './Card';
However if you really do want to export a default object that holds one or more components as properties, then import it accordingly:
import Components from './index.js';
// use Components.Cards were necessary
See the export documentation on MDN for more information.
Two solution here. Firstly when you assign a component into the Components object you can assign Card as a component like below.
Components['Card'] = <Card/>
and then use it like this
<div>{Components.Card}</div>
Secondly and My favorite is, whatever you have done in the index.js file to store the component is ok. But when you are using it use it like below.
<Component.Card/>
Example

How to wrap every exported comopnent with HOC?

I need to add to ALL of my React function components the possibility to add [data-test-id] attribute for testing purposes. To achieve that I created withTestId() HOC which adds optional prop testId to wrapped component and when it's defined it adds [data-test-id] to final HTML.
So when I define component like:
<ExampleComponent testId="example" />
it returns:
<div data-test-id="example" />
The only problem I have is to apply it to every component without the necessity to wrap it individually in every component. So instead of writing code like:
function ExampleComponent() { ... }
export default withTestId(ExampleComponent)
I would like to wrap all of my exports in my index.ts file, which right now looks like this:
export { default as ExampleComponent } from "./ExampleComponent";
export { default as ExampleComponent2 } from "./ExampleComponent2";
...
How can I achieve this?
I see two ways of doing this; One dynamic way, making the user-code of your library a bit more convoluted. with you being able to change the implementation easily and another one with a bit more boilerplate code, keeping the user-code as it is.
I haven't tested their behavior regarding tree-shaking when bundling the code.
Using destructing in user-code
This allows to add / remove things from your main component export file without having to worry about additional boilerplate in your library. The higher-order-component can be switched on/off easily. One caveat: The user code needs to use destructuring to retrieve the components.
Your new index.ts file would look like this, while I've called your previous index.ts file components.ts in the same directory:
import * as RegularComponents from "./components";
import withTestId from "./with-test-id";
const WithTestIdComponents = Object
.keys(RegularComponents)
.reduce((testIdComps, key) => {
return {
...testIdComps,
[key]: withTestId(RegularComponents[key])
};
}, {});
export default WithTestIdComponents;
To use it in your application code:
import MyComponents from "./components/tested";
const { Component1, Component2, Component3, Component4 } = MyComponents;
This uses the default export to make it look like you have all components in one place, but since you cannot destructure exports directly, you need this second step to get the correct components out of it.
Add boilerplate to the export file
Since there is an index.ts file with all the components exported in the library, one could import/rename each component and re-export them with withTestId and their name:
import withTestId from "./with-test-id";
import { default as TmpComponent1 } from "./component1";
import { default as TmpComponent2 } from "./component2";
import { default as TmpComponent3 } from "./component3";
import { default as TmpComponent4 } from "./component4";
export const Component1 = withTestId(TmpComponent1);
export const Component2 = withTestId(TmpComponent2);
export const Component3 = withTestId(TmpComponent3);
export const Component4 = withTestId(TmpComponent4);
This way, imports can be used as before:
import {
Component1,
Component2,
Component3,
Component4
} from "./components";
I'd argue that using index files already is some kind of boilerplate and this approach adds to it. Since the user code does not need any changes, I'd favor this approach.
In one of our projects, we have used a custom takeoff script to create this kind of boilerplate for us, whenever we generate a new component.
Examples
Here is a code sandbox to see both approaches.

How to get variable (const?) value in other component in ReactJS?

I starting using React. I have a main file (App.js) where all logic comes and in that file, I have added routes (, , ) which loading different page by URL from other files (only content).
In App.js constructor I added the web3 package and write that in a variable (window, global variable) because that is only one way to get that variable in other components files. I don't want to use window variables, because every user can see that. I want to use const/var variables, but get that values in every single component files, but I don't want to load same web3 library (or to connect smart contracts in every single component file). How to do that?
Assuming you mean everything web3 has. On your component file
import React from 'react';
import Web3 from 'web3';
const ComponentA () => {
// Do what ever you want with Web3 and react code here
};
export default ComponentA;
If you want the instance of web3 you created, maybe you could initialize web3 in its own file.
your own web3_instance.js
import Web3 from 'web3';
const web3Instance = new Web3();
export default web3Instance;
Then import it to any component.
import React from 'react';
import web3Instance from '../path/to/web3instance.js';
const ComponentA () => {
// Do what ever you want with web3Instance and react code here
};
export default ComponentA;

Inline import export component throwing error on hot reloading

For context, let me try to explain a little more.
In my project I have a folder, as example, for components.
Inside that folder I have my components files, and an index.js file where I import all the components and export than in the same line as follows:
export { default as Button } from './button'
export { default as Loader } from './loader'
export { default as ImageBackground } from './image-background'
So than I can import these components in Screen Component like that:
import { Button, Loader, ImageBackground } from 'src/components'
If I edit the components file, save and reload the project, everything works fine.
The problem is that when I edit any of these components with the Hot Module Replacement (Hot Reloading) actived, when it is triggered after an edit, it throws the following error:
Unhandled JS Exception: Requiring module "src/components/index.js", which threw an exception: TypeError: Cannot redefine property: Button
Has anyone have any idea why this is happening?
Thanks in advance!
Obs: When I import the component direct without using the index.js or if inside the index.js, I first import the component, than I assign the component to a variable and than I export this variable, it works fine.
my problem was solved when I changed render = () => (...) to render(){ return (...)} in react component

Categories

Resources