I have a plain javascript website. I'm importing with webpack a react component and use it in the website.
The problem is the component only works if I import react(globally).
I only use this component once in a while so I only want to import react if needed.
I'm interested in such a thing:
if(some_condition){ import react and import my component}
How could I do this?
Thanks
You can do dynamic imports like this:
(async () => {
if (somethingIsTrue) {
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
}
})();
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports
You can use dynamic import.
if (condition) {
Promise.all([
import("react-dom"),
import("react"),
import("path/to/component"),
])
// if you use named export for component, just use name instead of `default`
.then(([{ render }, { createElement }, { default: Component }]) => {
// select element where you want to render
let root = document.getElementById("root");
// render your component
render(createElement(Component), root);
});
}
Related
I want to create a list of functionalities that I can access easily from anywhere on my app by simply importing the component. Here is what my component looks like:
functionalities.component.jsx
import { AES } from "crypto-js"
import { useContext, useState } from "react"
import { ConfigurationContext } from "../env"
const {configurationState} = useContext(ConfigurationContext);
const Functionalities = {
encrypt: (info) => AES.encrypt(info, configurationState.application.key).toString();
}
export default Functionalities
The problem I'm facing now is that I'm not able to use any context values since it would cause an error. Is there a way to implement "useContext" on this?
You can call a React Hook only inside a React component or inside a custom hook, it's one of the rules of the hooks.
The best you could do, if you need to share common functionalities, is creating a set of custom hooks.
import { AES } from "crypto-js"
import { useContext } from "react"
import { ConfigurationContext } from "../env"
const Functionalities = {
useEncrypt: () => {
const { configurationState } = useContext(ConfigurationContext);
return (info) => AES.encrypt(info, configurationState.application.key).toString();
}
};
export default Functionalities;
Example usage (always remember to call useContext inside a Context.Provider).
function EncryptComponent({info}) {
const encrypt = Functionalities.useEncrypt();
return <button onClick={() => encrypt(info)}>Encrypt</button>
}
I provide a CodeSandbox example that show how to do that.
I'm using react, so before each test I need to create a container element. Currently, I have something like this:
import { screen } from '#testing-library/react';
import { render, unmountComponentAtNode } from 'react-dom';
import Form from './Form';
let container: Element | null = null;
// setup a DOM element as a render target
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
// cleanup on exiting
afterEach(() => {
unmountComponentAtNode(container!);
container?.remove();
container = null;
})
test('renders with a heading', () => {
render(
<Form heading={'Form Heading'} />, container
);
const element = screen.getByText(/form heading/i);
expect(element).toBeInTheDocument();
});
As you probably expected, I need to do the beforeEach and afterEach setup for each test file. Can this be made global(after being global, we still need access the to container variable)? Looking forward to your reply!
While this isn't technically the answer to the question you asked, this is the solution to the underlying problem you have.
You are importing the wrong render:
import { render, unmountComponentAtNode } from 'react-dom';
The render function imported from react-dom is used to actually render the result to the browser, not for testing.
You need to: import {render} from '#testing-library/react'. The testing library does all that container management for you.
If you actually need the container for anything, you can still do:
const {container} = render(/* ... */);
But most of the time that is not necessary.
I'm working on a project with nuxt.js and I want to implement the atomic design methodology
so I currently import the components like this
import ButtonStyled from '#/components/atoms/ButtonStyled.vue'
import TextLead from '#/components/atoms/TextLead.vue'
import InputSearch from '#/components/atoms/InputSearch.vue'
but I need to import like this
import {
ButtonStyled,
TextLead,
InputSearch
} from '#/components/atoms'
the closer I got was that,
/atoms/index.js
const req = require.context('./', true, /\.vue$/)
const modules = {}
req.keys().forEach(fileName => {
const componentName = fileName.replace(/^.+\/([^/]+)\.vue/, '$1')
modules[componentName] = req(fileName).default
})
export const { ButtonStyled, TextLead } = modules
but I'm still defining the export variable names statically, I need to define dynamics based on the components inside the folder
NOTE: I can not use
export default modules
if I use the above code snippet I will not be able to import the way I need it, which is:
import { ButtonStyled } from "#/components/atoms"
require.context is a quite obscure function in Webpack, you will have issues while running unit tests. But, to solve your problem; You will need to import the index.js file in the main.js of your project.
This is how I do it:
_globals.js
// Globally register all base components prefixed with _base for convenience, because they
// will be used very frequently. Components are registered using the
// PascalCased version of their file name.
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context('.', true, /_base-[\w-]+\.vue$/)
requireComponent.keys().forEach(fileName => {
const componentConfig = requireComponent(fileName)
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\/_base/, '').replace(/\.\w+$/, ''))
)
Vue.component(componentName, componentConfig.default || componentConfig)
})
components/index.js
//...
import './_globals'
//...
main.js
//...
import './components' // This imports in the index.js
//...
This way your components loaded in with require.context() gets registered as a vue component and made globally available. I advice to only use global components with components that will be used a lot. Do not load a component globally if you intend to use it only one time.
You can find a working example here -> https://github.com/IlyasDeckers/vuetiful/tree/master/src
To get your unit tests working with jest, you will need to mock require.context(). This was a true pain, but can be achieved easily by using babel-plugin-transform-require-context
I try to use your way to do that, and known you have make a mistake at module.exports
module.exports can not use import , i think you may can do like this
at atoms/index.js
const req = require.context("./", true, /\.vue$/);
const atoms = {};
req.keys().forEach(fileName => {
const componentName = fileName.replace(/^.+\/([^/]+)\.vue/, "$1");
atoms[componentName] = req(fileName).default;
});
export default atoms;
at where to use
import k from "#/components/atoms/index.js";
export default {
components: {
test1: k.test1,
test2: k.test2
}
};
or index.js
import test1 from "./test1.vue";
import test2 from "./test2.vue";
export { test1, test2 };
and where to use like this
import {test1,test2} from "#/components/atoms/index.js";
export default {
components: {
test1,
test2
}
};
I created a library that does all this for me, maybe it helps other people.
named-exports
I have a situation to convert several jQuery components to VueJs.
In general, I know what to do, but in some cases, I need to replace some functions calls.
For instance:
Component
const Component = (function () {
const initialize = () => {
return 'Tony Stark'
}
return {
initialize: initialize
}
})
export default Component
Random file, using exported function
$( document ).ready(function() {
Component.initialize()
});
What is the best solution to Component.initialize() still working?
Because I have this request in several files.
I got a solution:
import Component from './component'
// Call method
Component.methods.method()
You may import the component to every Vue component and use it like this:
import someComponent from './someComponent'
export default {
created () {
Component.initialize()
}
}
Or you could use instance properties, see https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
Im very new to react/jest. Im trying to test a very simple react component that gets data from the server and renders the response. My component looks like the below:
export default class myComponent extends Component {
constructor(props) {
super(props);
}
async componentDidMount() {
try {
let response = await axios.get(`server/url/endpoint`);
this._processSuccess(response.data);
} catch(e) {
this._processFail(e);
}
}
_processSuccess(response) {
this.setState({pageTitle: response.data.title, text: response.data.text});
}
render() {
return (
<div className="title">{this.state.pageTitle}</div>
);
}
}
Now I want to test this class. While I test:
I want to make sure componentDidMount() was not called
I want to pass test data to _processSuccess
Finally check the if the rendered output contains a div with class title that has the inner text same as what I supplied as response.data/pageTitle
I tried something like the below:
import React from 'react'
import MyComponent from './MyComponent'
import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme'
describe('MyComponent', () => {
it('should display proper title', () => {
const c = shallow(<MyComponent />);
c._processSuccess(
{data:{pageTitle:'siteName', test:'text'}}
);
// couldn't go further as Im getting error from the above line
});
});
But, Im getting MyComponent._processSuccess is not a function error. What would be the proper way to do that.
shallow() returns an Enzyme wrapper with some utils method to test the rendered component. It does not return the component instance. That's why you get the error when calling c._processSucces(). To access the component you can use the .instance() method on the wrapper, so the following should work:
const c = shallow(<MyComponent />);
c.instance()._processSuccess(
{data:{pageTitle:'siteName', test:'text'}}
);
In order to avoid that component's componentDidMount() get called, you can try settings disableLifecycleMethods on the shallow renderer, but I'm not sure about that because here Enzyme's documentation is not 100% clear:
const c = shallow(<MyComponent />, {
disableLifecycleMethods: true
});
Finally, you can check if the output contains the expected <div>, using Enzyme's contains() and one of Jest assertion methods:
expect(c.contains(<div className="title" />)).toBe(true);