Web3 with Webpack Build not found without webpack-dev-server - reactjs

With a Reactjs webpack project I am able to run webpack dev server and access my index.html with web3 picked up.
If I build the project and open the index.html in Chrome then web3 is not detected.
Everything works when running webpack-dev-server --mode development --open --hot
but with webpack --mode development then web3 is not injected
The purpose of my app is a tool to be run locally, it does not have to be served from anywhere public, also I don't see that I need to run a lite server to serve the content.
web3: 1.0.0-beta.36
webpack: 4.22.0
webpack-cli: 3.1.2
webpack-dev-server: 3.1.8
import './index.css';
import IxoTimelockApp from './components/IxoTimelockApp';
import InstallMetaMask from './components/install-
metamask/install-metamask-component.jsx';
let regeneratorRuntime = require("regenerator-runtime");
class App extends Component {
state = {
web3Obj:null
}
componentDidUpdate(prevprops) {
if (prevprops != this.props){
this.setState({web3Obj: this.props.web3Obj})
}
}
componentDidMount(){
window.addEventListener('load', async () => {
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum);
try {
// Request account access if needed
await ethereum.enable();
this.setState({web3Obj: window.web3})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
window.web3 = new Web3(web3.currentProvider);
this.setState({web3Obj: window.web3})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
});
}
render() {
if(this.state.web3Obj) {
return <TimelockApp/>
}else return <InstallMetaMask/>
}
}
export default App;
const wrapper = document.getElementById("root");
wrapper ? ReactDOM.render(<App />, wrapper) : false;

From: MetaMask Compatibility Guide
Requirements 🔩
🌐 Http(s) - Web Server Required
Due to browser security restrictions, we can't communicate with dapps
running on file://. Please use a local server for development.

Related

How to add workbox to React post update

I have decided to try to use workbox but all of the guides i have seen talk about integrating with the service worker that react makes.
But when i install a CRA i no longer get the service worker made for me. What do i need to do to integrate workbox here?
This is my current code:
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import swDev from './swDev'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
swDev()
swDev.js
export default function swDev(){
let swURL = `${process.env.PUBLIC_URL}/sw.js`;
if('serviceWorker' in navigator){
navigator.serviceWorker.register(swURL).then(res => {
console.log('Service worker has been registered');
}).catch(err => console.log('Service worker was not registered'))
}
}
then this is the service worker in the public file
const cacheVersion = 'v1'
self.addEventListener('install', ()=>{
console.log('Service worker has been installed');
})
self.addEventListener('activate', ()=>{
console.log('Service worker is being activated');
})
self.addEventListener('fetch',(e)=>{
e.respondWith(handleRequest(e.request))
// function to update the cache
// updateCache(e.request)
})
async function handleRequest(req){
const cache = await caches.open(cacheVersion)
const cacheRes = await cache.match(req)
if(cacheRes){
return cacheRes;
}else{
const netRes = await fetch(req);
console.log(netRes);
if(netRes.ok){
return netRes;
}else{
// return an error message or something by matching it to a 404 page
return netRes;
}
}
}
async function updateCache(req){
if(navigator.onLine){
const res = await fetch(req);
const cache = await caches.open(cacheVersion);
if(res.ok){
// add the response to the caches and return the response
await cache.put(req, res.clone())
}
}
}
create-react-app v4 will check for the presence of a src/service-worker.js file at build time, and if found, run workbox-webpack-plugin's InjectManifest plugin, passing in that file as the swSrc parameter.
If you're starting a new project and follow the instruction from create-react-app's "Making a Progressive Web App" guide, i.e. you run npx create-react-app my-app --template cra-template-pwa, you'll end up with everything in the right place.
Which is to say your project will:
automatically bundle the code in src/service-worker.js (transforming the ES module imports into code that can be run inside the service worker)
look for the symbol self.__WB_MANIFEST somewhere inside your src/service-worker.js, and replace it with a precache manifest, consisting of URLs and revision info about all your webpack assets, so that Workbox can precache them.
If you're not interested in precaching your webpack assets, then you don't need to use the InjectManifest plugin at all, and you can just put whatever code you want in a file named anything other than src/service-worker.js, and register that file as your service worker. That's up to you.
If you are interested in Workbox's precaching, but you're upgrading from an older create-react-app and you don't have a "correct" src/service-worker.js file, you can manually copy the file from the template into your project.

Nx NextJS Runtime config variables are "undefined" while running production build

Nextjs app that is created via Nx Nextjs schematics gives undefined for publicRuntimeConfig accessed in getInitialProps during runtime of production build but in development mode it works perfectly
next.config.js
module.exports = { serverRuntimeConfig: { // Will only be available on the server side mySecret: 'secret', secondSecret: process.env.SECOND_SECRET // Pass through env variables }, publicRuntimeConfig: { // Will be available on both server and client staticFolder: '/static' } };
getInitialProps
import getConfig from 'next/config'; const { publicRuntimeConfig } = getConfig(); ... Index.getInitialProps = async ctx => { console.log('I am here'); console.log(publicRuntimeConfig.staticFolder); // Undefined when running prod build return {}; };
Orignal Github Issue Here

How to initialize firebase in gatsby?

I'm stuck on making firebase work in my gatsby application that uses Redux with Redux-sagas. I know about the existence of firebase-sagas but I'm trying to make without using it.
I'm trying to init firebase auth by:
import * as firebase from 'firebase/app';
import 'firebase/auth';
export const app = firebase.initializeApp(
{
apiKey : "apiKey",
authDomain : "project.firebaseapp.com",
databaseURL : "https://project.firebaseio.com",
projectId : "project",
storageBucket : "project.appspot.com",
appId : "appId"
}
)
export const authRef = () => app.auth(); //also tried: firebase.auth() and firebase.auth(app)
//firebase.auth returns a function, but firebase.auth() throws error
I have the following config on my gatsby-node.js:
const path = require('path');
exports.onCreateWebpackConfig = ({ actions, plugins, loaders, getConfig }) => {
const config = getConfig()
config.resolve = {
...config.resolve,
mainFields: ['module', 'browser', 'main'],
alias: {
...config.resolve.alias,
['firebase/app'] : path.resolve(__dirname, 'node_modules/firebase/app/dist/index.cjs.js'),
['firebase/auth'] : path.resolve(__dirname, 'node_modules/firebase/auth/dist/index.cjs.js'),
}
}
actions.replaceWebpackConfig(config)
}
It trows the error:
{ [M [Error]: The XMLHttpRequest compatibility library was not found.]
code: 'auth/internal-error',
message: 'The XMLHttpRequest compatibility library was not found.' }
I think it's some problem related to webpack. I would love any insights on this problem :)
As Gatsby builds pages in a server environment, you can't access Firebase during Gatsby build time. Firebase calls (using the Web SDK) have to happen when the user is on a browser/client environment.
One solution to this problem is creating a function like so:
firebase.js:
import firebase from '#firebase/app';
import '#firebase/auth';
import '#firebase/firestore';
import '#firebase/functions';
const config = {
... firebase config here
};
let instance;
export default function getFirebase() {
if (typeof window !== 'undefined') {
if (instance) return instance;
instance = firebase.initializeApp(config);
return instance;
}
return null;
}
This file returns a function, which returns an instance of Firebase if the user has the global window available (e.g. on the browser). It also caches the Firebase instance to ensure it cannot be reinitialised again (in case of the user changing page on your website).
In your components, you can now do something similar to the following:
import getFirebase from './firebase';
function MyApp() {
const firebase = getFirebase();
}
As Gatsby will try to build this page into HTML during gatsby build, the firebase const will return null, which is correct, as the Firebase Web SDK cannot initialise on a server environment. However, to make use of Firebase on your website, you need to wait until Firebase is available (so the user has to have loaded your website), so we can make use of Reacts useEffect hook:
import React { useEffect } from 'react';
import getFirebase from './firebase';
function MyApp() {
const firebase = getFirebase();
useEffect(() => {
if (!firebase) return;
firebase.auth().onAuthStateChanged((user) => { ... });
}, [firebase]);
}
This works as Firebase is being used in a browser environment and has access to the browser, which is needed for the Web SDK to work.
It does have drawbacks; your compo have to return null in instances when you need Firebase to display content, which will mean your HTML build on the server will not contain any HTML, and it'll be injected via the client. In most cases though, such as an account page, this is fine.
If you need access to data from say Cloud Firestore to display page content, you're best using the Admin SDK to fetch content and add it to GraphQL during Gatsby build. That way it will be available on the server during build time.
Sorry if that was a waffle or not clear!

Integrating web3 from Metamask in React

I am new to ReactJS.
Seem to be having trouble integrating web3 from Metamask in React.
Metamask version: web3#1.0.0-beta.34
import Web3 from 'web3'
let web3;
window.addEventListener('load', function () {
if (typeof window.web3 !== 'undefined') {
web3 = new Web3(window.web3.currentProvider);
} else {
// No web 3 provider
console.log("Please install Metamask");
}
});
export default web3;
Getting the following error:
window is not defined
ReferenceError: window is not defined
at Object../lib/getWeb3.js (lib/getWeb3.js:5:0)
window is not defined on Server, only in client's browser, hence you can't use MetaMask server-side. However, you can connect to INFURA when you want to use web3 in your React component server-side or without MetaMask support.
The simplest way is to use react-web3-provider component.
Add the Web3Provider to your root React component:
import Web3Provider from 'react-web3-provider';
ReactDOM.render(
<Web3Provider
defaultWeb3Provider="https://mainnet.infura.io/YOUR_API_KEY"
loading="Loading..."
>
<App />
</Web3Provider>
)
Then in component where you want to use Web3:
import { withWeb3 } from 'react-web3-provider';
class MyComponent {
render() {
const { web3 } = this.props;
web3.eth.getAccounts(console.log);
// Version 1.0.0-beta.35
return "Web3 version: {web3.version}";
}
}
export default withWeb3(MyComponent);

create-react-app for server-side rendering

Im using create-react-app tool for building my first react application with react-routes and now I would like to use server side rendering to avoid loading all pages at once.
I followed guides and installed express.js, separated client-side and server-side with .js files and run it with
NODE_ENV=production babel-node --presets 'react,es2015' src/server.js
But I get an error when app is trying to compile sass #import statements. I think I have to serve assets first, but I don't know how to insert webpack functions in server.js logic
create-react-app also has npm run build command for production build and create js and css files, so maybe there is some way to skip assets parts while compiling server.js ?
Server.js file contents
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import NoMatch from './pages/NoMatch';
// initialize the server and configure support for ejs templates
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, 'static')));
// universal routing and rendering
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(<RouterContext {...renderProps}/>);
} else {
// otherwise we can render a 404 page
markup = renderToString(<NoMatch/>);
res.status(404);
}
// render the index template with the embedded React markup
return res.render('index', { markup });
}
);
});
// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
Try installing node-sass and following the official guide
https://create-react-app.dev/docs/adding-a-sass-stylesheet/

Resources