Environmental specific imports - reactjs

Hey guys my company has the task that we are rebuilding our entire app and website in react from a 10 year old mvc thing...
They want to export some of the components from the new site into the existing site while we rebuild. What I was looking for is whether there is a webpage plugin or loader that acted like metro so I could say import * from 'package' and if I wanted to slightly customise it I could have 'package.old.ts' and 'package.ts' if package.old exists then it will import that otherwise it will do package?

Webpack's built in resolve.extensions
If by package you mean a module (a ts/tsx file in your project) you can solve this by using Webpack's resolve.extensions configuration option.
Webpack will always evaluate this array left to right, the first one found wins. Therefore if you define the array as: ['.old.ts', '.ts', /*others*/] it will try to match package.old.ts before package.ts
Working example:
package.old.ts:
export const message = "Hello old";
package.ts:
export const message = "Hello new";
index.ts:
import {message} from './package';
console.log(message);
This will output
Hello old.
If you remove or rename package.old.ts it outputs new. If you want to change the priority all you have to do is reorder the array in your webpack.config.js

Related

React : webpack.config.js modification to import json variables in a sass file?

I am trying to import variables from .json file in a .scss file with node-sass-json-importer package.
I am facing a problem because this package is not automatically integrated in react-scripts/config/webpack.config.js. So, I would like to modify this file as follows below :
Add const jsonImporter = require ('node-sass-json-importer');
Add an optional preProcessorOptions object parameter to getStyleLoaders function. Indeed, this function has no preprocessor options const getStyleLoaders = (cssOptions, preProcessor) and the only option added by default is sourceMap: true. Of course, this function will take in account this new parameter.
Add a third parameter in the getStyleLoaders call for scss file.
{
implementation: require("sass"),
sassOptions: {
importer: jsonImporter(),
}
}
It works on a minimal webpack implementation (without react). But, I suppose it is not so easy to apply changes to react-scripts/config/webpack.config.js and I suspect I will have many problems. Perhaps, there is an another way to do it.
Thanks for answer.

test database for react-native app development

I'm in the early stages of developing an app with react-native, and I need a DB implementation for for testing and development. I thought that the obvious choice would be to use simple JSON files included with the source, but the only way I see to load JSON files requires that you know the file name ahead of time. This means that the following does not work:
getTable = (tableName) => require('./table-' + tableName + '.json') // ERROR!
I cannot find a simple way to load files at runtime.
What is the proper way to add test data to a react-native app?
I cannot find a simple way to load files at runtime.
In node you can use import() though I'm not sure if this is available in react-native. The syntax would be something like:
async function getTable(tableName){
const fileName = `./table-${tableName}.json`
try {
const file = await import(fileName)
} catch(err){
console.log(err
}
}
though like I said I do not know if this is available in react-natives javascript environment so ymmv
Unfortunately dynamic import not supported by react-native but there is a way so to do this
import tableName1 from './table/tableName1.json';
import tableName2 from './table/tableName2.json';
then create own object like
const tables = {
tableName1,
tableName2,
};
after that, you can access the table through bracket notation like
getTable = (tableName) => tables[tableName];

How to do React Native-style multiple version builds for React with Webpack

Motivation
I am maintaining an app that is white-labelled for numerous separate brands, which vary mainly in style but also sometimes in core UX. The current (Backbone) solution involves keeping shared code in a separate repo and then building the separate apps with Grunt, with much of the style code and some view overrides for each project living in its own folder. We simply run all the grunt tasks one after the other using a shell script. We're going to build new versions of this thing in React going forward and want to minimize duplicate code, which has now become a major problem in the legacy version.
Desired outcome
The React Native packager builds two versions of its app at the same time. It looks at an import statement like import ComponentA from './ComponentA.js' and goes looking for either ComponentA.android.js or ComponentA.ios.js first, then falls back to importing ComponentA.js if it doesn't find a platform-specific one. I would like to replicate this behavior in Webpack. So I would like to have a folder that looks like this:
react_clients/src/components
|_ ComponentB.js // import ComponentA from './ComponentA.js';
|_ ComponentA.js
|_ ComponentA.brand1.js
|_ ComponentA.brand2.js
Webpack should build ComponentB.js as follows:
brand1.bundle.js imports from ComponentA.brand1.js
brand2.bundle.js imports from ComponentA.brand2.js
brand3.bundle.js and brand4.bundle.js import from ComponentA.js
This would also apply to styles, ideally with the same naming convention.
If necessary, Webpack could be run separately for each version, either using different webpack.config files or accepting command line arguments. The key thing is to avoid duplicating application code.
Current code
The starting point for Webpack is a freshly-generated and ejected create-react-app project.
PS: Apologies in advance if this turns out to be a duplicate but this has been a very tricky question to research. I suspect the answer will have something to do with an advanced configuration of https://webpack.js.org/configuration/resolve/ but can't figure it out yet.
Alright folks here's what I ended up doing:
Dev
In .env.development I specify a variable for the name of the project I want to do dev on:
REACT_APP_VERSION_NAME=brand1
Then in webpack.config.dev.js I take advantage of module resolution to achieve the behavior described above:
const JS_PROJECT_EXTENSION = `.${process.env.REACT_APP_VERSION_NAME}.js`;
const STYLE_PROJECT_EXTENSION = `.${process.env.REACT_APP_VERSION_NAME}.pcss`;
const extensions = [JS_PROJECT_EXTENSION, '.js', '.json', '.jsx', STYLE_PROJECT_EXTENSION, '.pcss'];
...
module.exports = {
...
extensions,
...
}
Then in the code I can simply do the following:
import ComponentA from './componentA';
import Styles from './styles';
And everything works as expected.
Production
I don't specify REACT_APP_VERSION_NAME in .env.production. Instead the relevant config files export functions, and I iterate over the versions I want to build.
First, I created a separate version of config/paths.js that exports a function instead of a static object:
module.exports = function(projectName) {
return {
...
appBuild: resolveApp('build/' + projectName),
...
};
}
And my webpack.config.prod.js looks like this:
...
const getPaths = require('./paths.prod');
...
module.exports = function(projectName) {
const paths = getPaths(projectName);
const JS_PROJECT_EXTENSION = `.${projectName}.js`;
const STYLE_PROJECT_EXTENSION = `.${projectName}.pcss`;
const extensions = [JS_PROJECT_EXTENSION, '.js', '.json', '.jsx', STYLE_PROJECT_EXTENSION, '.pcss'];
...
return {
...
output: {
...
filename: projectName + '-assets/js/[name].[chunkhash:8].js',
chunkFilename: projectName + '-assets/js/[name].[chunkhash:8].chunk.js',
...
}
... [etc, adding projectName to any output that needs to be built separately]
};
}
Finally, just wrap most of the action in scripts/build.js in a loop:
...
[various imports]
...
process.argv[2].split(' ').forEach(projectName => {
const config = require('../config/webpack.config.prod')(projectName);
const paths = require('../config/paths.prod')(projectName);
...
[rest of build.js as normal]
}
After that it's just a matter of pointing your server at the right files for each version, and running yarn build "brand1 brand2" when you want to build.
Going to accept this answer since it's working for me for now but would love to hear about potential improvements from anyone who comes across it in the future.

react storybook addon knobs not showing

I cant seem to be getting the #storybook addon knobs working? It doesnt seem to be decorating the actual story. Pretty much followed this
My code below.. Im using getstorybook with create-react-app
Using below packages:
#storybook/addon-actions": "^3.1.2,
#storybook/addon-info": "^3.1.4,
#storybook/addon-knobs": "^3.1.2,
#storybook/react": "^3.1.3
my setup
//.storybook/addons.js
import '#storybook/addon-knobs/register'
//.config
import { configure, setAddon, addDecorator } from '#storybook/react';
import infoAddon from '#storybook/addon-info';
setAddon(infoAddon);
function loadStories() {
require('../stories');
}
configure(loadStories, module);
//stories/index.js
import React from 'react';
import { withKnobs, text, boolean, number } from '#storybook/addon-knobs';
import { storiesOf } from '#storybook/react';
const stories = storiesOf('Storybook Knobs', module);
// Add the `withKnobs` decorator to add knobs support to your stories.
// You can also configure `withKnobs` as a global decorator.
stories.addDecorator(withKnobs);
// Knobs for React props
stories.add('with a button', () => (
<button disabled={boolean('Disabled', false)} >
{text('Label', 'Hello Button')}
</button>
))
This should be a no brainer, but no suck luck.
Hope this helps someone, but for some reason my addons panel suddenly disappeared from view and I couldn't figure out how to get it back. I could see the addons markup being rendered in the "elements" pane in my dev tools - so I knew things were working. Somehow storybook stored a bad value in my localStorage['storybook-layout'] so that the addons were positioned waaaaayyy off screen. Running the following fixed it.
localStorage.removeItem('storybook-layout')
You probably need to create the addons.js file on the storybook config folder. (By default .storybook).
Check the Docs for knobs you need to add the following:
import '#storybook/addon-knobs/register';
Hitting D on your keyboard toggles the layout // Ray Brown
or
You should also be able to expand the sidebar by clicking and dragging on the right side.
Try removing all the query string on the url
eg. http://localhost:6006/?knob-is_block=false&knob-Disabled=false&knob-disabled=false&knob-Style%20lite=false&knob-show=true&knob-Size=md&knob-readOnly=false&knob-Style=default&knob-icon%20name=vertical&knob-Label=Hello%20Button&knob-Active=false&knob-is_loading=false&selectedKind=Button&selectedStory=default%20style&full=0&addons=0&stories=1&panelRight=0&addonPanel=storybooks%2Fstorybook-addon-knobs
to http://localhost:6006
This worked for me.
In Storybook 6.5 I was facing the same issue after manually adding the addon:
npm i #storybook/addon-knobs --dev
All I needed to do is to go to the main.js file inside the .storybook folder and add the following:
"addons": ["#storybook/addon-knobs"]
Then I stopped the already running storybook in the terminal using ctrl/cmd + c and then re-run the storybook:
npm run storybook
Hope it helps. Thank you.

Setting up PDF.js to work with Meteor + Reactjs project (want to use text layer)

I've been working on a project using Meteor and React, which needs a PDF viewer with the ability to select text.
I'm currently trying to achieve this with Mozilla's PDF.js, but am having some trouble getting started. I'm a long time reader, first time asker at stackoverflow.
I've installed PDF.js with npm.
npm install pdfjs-dist --save
Now I'm trying to modify the example from pdf.js's github project here to create a React component that will render a PDF from a supplied file path and include a text layer.
imports/ui/components/PDF/PDFText.jsx
import React from 'react';
require ('pdfjs-dist/build/pdf.combined');
require ('pdfjs-dist/web/compatibility');
export default class PDFText extends React.Component {
renderPDF() {
PDFJS.workerSrc = '/node_modules/pdfjs-dist/build/pdf.worker.js';
const container = document.getElementById('pdf-container');
const scale = 1;
const pageNumber = 1;
PDFJS.getDocument(this.props.file).then(function(pdf) {
return pdf.getPage(pageNumber).then(function(page) {
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: pageNumber,
scale: scale,
defaultViewport: page.getViewport(scale),
textLayerFactory: new PDFJS.DefaultTextLayerFactory()
});
pdfPageView.setPdfPage(page);
return pdfPageView.draw();
});
});
}
render() {
this.renderPDF()
return (
<div id='pdf-container'></div>
);
}
}
If I include this component in page I get the following error:
Uncaught (in promise) TypeError: PDFJS.DefaultTextLayerFactory is not a constructor
The next thing I tried was including 'pdfjs-dist/web/pdf_viewer' in my code, as this is where DefaultTextLayerFactory is declared. I modified the code above to add the following line above the class declaration:
require ('pdfjs-dist/web/pdf_viewer');
When I run the code now, I get a different error.
Uncaught TypeError: Cannot read property 'PDFJS' of undefined
at Object.<anonymous> (modules.js?hash=9dd20a3…:114918)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at Object.<anonymous> (modules.js?hash=9dd20a3…:117449)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at Object.<anonymous> (modules.js?hash=9dd20a3…:118157)
at __w_pdfjs_require__ (modules.js?hash=9dd20a3…:114838)
at module.exports (modules.js?hash=9dd20a3…:114884)
at modules.js?hash=9dd20a3…:114887
at webpackUniversalModuleDefinition (modules.js?hash=9dd20a3…:114811)
at pdf_viewer.js (modules.js?hash=9dd20a3…:114818)
I'm really unsure what is going on here. I noticed that the function complaining refers to webpack - which I haven't been using.
I've also tried including the following check at the start of my code (this is taken from pageviewer.js in the github link above).
if (!PDFJS.PDFViewer || !PDFJS.getDocument) {
alert('Please build the pdfjs-dist library using\n' +
' `gulp dist`');
}
My code does in fact trigger that alert (PDFJS.PDFViewer is undefined) but the message doesn't seem correct as I installed the built pdfjs-dist library using npm. That message seems for people who cloned the repo. There isn't a gulp file in the pdfjs-dist directory - which makes sense.
I'm sure part of thep problem is that I'm experimenting with a lot of new tools here. This is my first time working with meteor, react, node, and pdf.js, so apologies in advance if I've made an obvious rookie mistake.
For the record I've tried a few other libraries, including:
mikecousins/react-pdf-js (worked reasonably well for simply displaying a pdf with no text layer).
peerlibrary/meteor-pdf.js (I hit some errors with this one as well, and I didn't pursue it too much further as the repo hasn't been touched in a couple of years).
Hopefully that's enough information for someone to spot the issue. My theory is that there's some other set up step I need to do to get this working for meteor or react (and that's why it hasn't been obvious from the "getting started" in the PDF.js website.
Also, I'm not locked in to PDF.js, so if the easiest solution to my problem is to use something else, I'd be happy to try that.
Thanks for your time

Resources