React router how to load css based on global var - javascript

I have a node project that creates a static react SPA using:
react-router
redux
postcss
react-css-modules
It currently loads in a global css file and modular css for each component. On start I fetch a global theme var that will be say 'theme-1' or 'theme-2'. I want to load different css files, same name but different directory path, based on the value of the theme var, Eg:
If theme is 'theme-1' then import '/css/themes/theme1/Component.css'.
Else If theme is 'theme-2' then import '/css/themes/theme2/Component.css'.
Any ideas how I could achieve this?
Note that module imports can not be wrapped in an if/else statement as far as I can see.

You could use require:
if(...) {
require('/path/to/theme1.css');
}
else {
require('/path/to/theme2.css');
}

Related

prismjs not working between different routes using vuerouter in vuejs

I've imported prism.js globally in main.js file.
Code block syntax highlighting working fine in Home components, but after routing to another page using vue-router, there is no effect.
in main.js
// Global Import
import 'prismjs/prism.js'
import 'prismjs/components/prism-swift.min.js' // swift lang
import './theme/prism-swift-theme.css'
in my about page component...
<pre><code class="language-swift">
private func setupSubviews() {
let value = "Loading code block";
}
</code></pre>
Unable to understand what's wrong here. Is there any way I can import node_modules prismjs files globally? I thought keeping main.js will work fine, but it's not adding globally between routes...
Once you install it with npm the best approach is to import it whenever it is used in each component seperately with import Prism from 'prismjs'. Just make sure to use the Prism.highlightAll() method in the component where you're using prism after the DOM is rendered whether in mount() hook or in the updated Vuejs hook using nextTick method to make sure all the DOM is rendered before using prism. So in your case you should use it this way:
import Prism from "prismjs"
updated: function () {
this.$nextTick(function () {
Prism.highlightAll();
})
}
make sure you call highlightAll in yor components seperately and not globaly.

Dynamic Imports Based on this.props in React

In my NextJS app, I have node_modules (js files) for themes for certain elements on the page. I want to load only the files I need this based on this.props.theme for the component. How/where would I do this in my component. Basically, if I have a bunch of files for themes:
theme-red.js
theme-orange.js
theme-yellow.js
theme-green.js
theme-blue.js
theme-purple.js
theme-violet.js
theme-pink.js
theme-rainbow.js
In the props, I would this.props.themepassed in and then load in require(theme-${this.props.theme}.js)
You can use the import function:
const theme = await import(./{path-to-the-files}/theme-${this.props.theme}.js)
It would be good to include this in the componentWillMount function of the component.
https://nextjs.org/docs/advanced-features/dynamic-import
https://javascript.info/modules-dynamic-imports

How do I register a Vue component?

I have the following files. All I want to do is to be able to create different components that are injected. How do I achieve this using require.js? Here are my files:
main.js
define(function(require) {
'use strict';
var Vue = require('vue');
var myTemplate = require('text!myTemplate.html');
return new Vue({
template: myTemplate,
});
});
myTemplate.html
<div>
<my-first-component></my-first-component>
</div>
MyFirstComponent.vue
<template>
<div>This is my component!</div>
</template>
<script>
export default {}
</script>
I'm going to assume you're using webpack as explained in the Vue.js docs, or else your .vue file is useless. If you're not, go check how to set up a webpack Vue app first, it's what lets you use .vue files.
import Menubar from '../components/menubar/main.vue';
Vue.component('menubar', Menubar);
That's how you add e.g. a menubar component to the global scope. If you want to add the component to just a small part of your app, here's another way of doing it (this is taken from inside another component, but can be used in exactly the same manner on your primary Vue object):
import Sidebar from '../../components/sidebar/main.vue';
export default {
props: [""],
components: {
'sidebar': Sidebar
},
...
You can load components without webpack, but I don't recommend it, if you're gonna keep using Vue (which I strongly suggest you do) it's worth it to look into using webpack.
Update
Once again, really, really, really consider using webpack instead if you're gonna be continuing with Vue.js, the setup may be slightly more annoying but the end result and development process is waaaay better.
Anyway, here's how you'd create a component without webpack, note that without webpack you can't use .vue files since the .vue format is part of their webpack plugin. If you don't like the below solution you can also use e.g. ajax requests to load .vue files, I believe there is a project somewhere out there that does this but I can't find it right now, but the end result is better with webpack than with ajax anyway so I'd still recommend going with that method.
var mytemplate = `<div>
<h1>This is my template</h1>
</div>`
Vue.component('mycomp1', {
template: mytemplate
});
Vue.component('mycomp2', {
template: `
<div>
Hello, {{ name }}!
</div>
`,
props: ['name'],
});
As you can see, this method is A LOT more cumbersome. If you want to go with this method I'd recommend splitting all components into their own script files and loading all those components separately prior to running your actual app.
Note that `Text` is a multi line string in javascript, it makes it a little easier to write your template.
And as I said, there is some project out there for loading .vue files using ajax, but I can't for the life of me find it right now.

Accessing webpack/bundled React Class from .html file

Is it not possible to access already 'built' components within the html file that the build is linked to?
I am trying the following -
In bundle.js
var React = require('react');
var ReactDOM = require('react-dom');
var Titles = React.createClass({
render() {
return (
<div>
<h1>{this.props.headerProp}</h1>
<h2>{this.props.contentProp}</h2>
</div>
);
}
});
In my html page -
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
<div id="con"></div>
<script type="text/javascript" src="/public/bundle.js'"></script>
<script type="text/jsx">
ReactDOM.render(<Titles headerProp = "Header from props..." contentProp = "Content
from props..."/>, document.getElementById('con'));
</script>
But console outputs React is not defined.
I have even tried to set react globally within the bundle -
window.React = React;
And calling it with window. prefixed but yields the same result.
Because you're mentiong a bundle.js file with a snippet containing commonjs style imports, I'm assuming you're using Webpack.
I have some considerations about your code.
bundle.js file will not expose any module as global. That includes React and any other module you might require inside the bundle. There isn't goint to be window.ModuleName. However, these module are accessible in the Browser via require.js because Webpack will export modules as UMD, that is, they will be accessible through either commonjs or AMD (Require.js).
I'm pretty sure that, if in the entry point of your webpack configuration file, you do something like var React = require("react"); window.React = React, that's actually going to work.
There's a Webpack module meant to expose modules globally (like in window.x) in a more ellegant way than (2) called expose-loader. You should take a look at it.
You should really try to avoid doing what you're trying to do. In your entry.js file (the entry point of your webpack configuration) should be responsible for doing something like ReactDOM.render(..., document.getElementById("#app")). So that, just by including your bundle, the app will render automatically. This is what everybody does.
JSXTransformer.js as well as the <script type="text/jsx"> have been deprecated a long time ago. Now you're supposed to use Babel to compile React.

Import or require a bundled js file created by react application

Let's say I have a normal react application using redux and some ajax calls.
If I want to pass it to someone I will give them the bundled js file I created with webpack and ask them to include it in their HTML + render a div with an id of "myApp" for example:
<div id="myApp"></div>
Ok, what if their website is also created with react, and they want to include my bundled js file inside one of their components, and of course render the relevant div?
I tried to use import or require to simulate this:
require('./path/to/myBundle.js');
import './path/to/myBundle.js';
Example:
//...
import './path/to/myBundle.js'; // the file that will render myApp to the relevant div
// ....
export function SomeApp(args){
return(
<div>
<div id="myApp"></div>
<SomeComponent />
</div>
);
};`
This does not work as I get some errors about:
Uncaught Error: Minified React error #37; visit
http://facebook.github.io/react/docs/error-decoder.html?invariant=37
for the full message or use the non-minified dev environment for full
errors and additional helpful warnings.
And when I visit this site I see:
_registerComponent(...): Target container is not a DOM element.
However, if they'll use this file (myBundle.js) outside their components (top level index.html for example) it will work just fine of course.
EDIT:
I forgot to mention that I think I know what the problem is, the application doesn't have the HTML ready with this div yet. but I don't know a good and native way to wait for it to exist.
EDIT #2 following #Frxstrem 's answer:
I'm trying to follow this answer but I think I'm doing it wrong.
I have 2 copies of corry house slingshot demo app as app1 and app2.
changed the 'output' on webpack.config.prod.js of app1 to:
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: 'app1Bundle.js',
library: "App1",
libraryTarget: "umd"
},
I'm trying to render app1 inside the homepage component of app2.
so i copied the "published" files from app1 to the root of app2 and called the folder app1, then added an import call:
import {app1} from '../../app1/app1Bundle';
and a matching tag inside the return function:
const HomePage = () => {
return (
<div>
<app1 />
<h1>App 2</h1>
</div>
);
};
I get the same error as I posted above.
I also tried different combinations:
import app1 from '../../app1/app1Bundle'; // without curly braces
or even just getting the script as a normal js script
import '../../app1/app1Bundle';
or
require('../../app1/app1Bundle');
and then tried to render a normal div tag with an id of "app1"
const HomePage = () => {
return (
<div>
<div id="app1"></div>
<h1>App 2</h1>
</div>
);
};
nothing seems to work as I still get the same error.
I think the problem is the timing of the script load and the rendering of the elements. I think the div does not exist yet when the bundled script is searching for it.
By default, Webpack will expose the entry module as a variable, which is useful when you include scripts with a <script> tag. (Because of this, if you require it you would likely just get {}.) However, if you want to load your bundle from other modules, you'll need to tell Webpack to expose it as an exported module instead.
The easiest way to do this is to set
{
...
"libraryTarget": "umd"
}
in your Webpack configuration. With that, Webpack knows that it should expose your entry module as a module that can be required in Webpack, but can also be loaded with a <script> tag as necessary.
Webpack libraryTarget documentation
The main problem i faced was to include the bundled js file of app1 after the DOM contains the target div it needs.
What i ended up doing was, creating a component in app2 project that will require() the bundled js file on componentDidMount() and will render and return the target div with a relevant id.
The reason i created a component is purely for re-usability purpose, instead of requiring this script with componentDidMount() on every component that needs it.
So, this is the component:
import React from 'react';
class AppOne extends React.Component {
componentDidMount() {
require('../app1/app1Bundle.js');
}
render() {
return (
<div id="app1"></div>
);
}
}
export default AppOne;
And this is how i use it in other component:
import React from 'react';
import AppOne from './AppOne';
const HomePage = () => {
return (
<div>
<h1>App 2 - wrapper for app1</h1>
<hr />
<AppOne />
<hr />
<h1>This is App2 as well </h1>
</div>
);
};
export default HomePage;
It's working fine. my only concern is that i may face some conflicts with react because i'm using 2 react apps though for ow i don't see any errors.
I guess that's an issue for a different question.
EDIT:
If someone will use this approach you should note that this will work only for the first load. because after the component will re-render itself the bundled script will not run again.

Categories