cannot import electron in react componment for ipcRenderer - javascript

So I am trying to import the ipcRenderer into a react component to communicate with the electron side. The issue is I cannot import electron. I tried
import { ipcRenderer } from 'electron/renderer'
returns module electron/renderer not found
import { ipcRenderer } from 'electron'
returns fs.existsSync is not a function
const renderer = require('electron');
returns fs.existsSync is not a function
const renderer = require('electron').ipcRenderer;
returns fs.existsSync is not a function
const renderer = window.require('electron');
returns window.require is not a function
I do not know what to do anymore, I have tried everything

I got it! using electron-react-bolierplate they prepared a custom preload.js script that exposes three functions to the rendered components: myPing: (Just a demo, send a ping message to the console) and exposes the on and once ipcRenderer methods
import { Component } from 'react';
...
...
class Hello extends Component {
componentDidMount() {
console.log('Mounted!', 'Window', window, 'electronApi', window.electron, 'ipcRenderer', window.electron.ipcRenderer);
window.electron.ipcRenderer.on('ipc-example', (arg) => {
// eslint-disable-next-line no-console
console.log(arg);
});
window.electron.ipcRenderer.myPing();
}
render() {
return (
<div>
...
...

I used to have same problem. You should not import electron directly inside your renderer/react component. Instead in your preload.ts file you are given some basic configuration by electron-react-bolierplate to use.
So inside your react component you should use window.electron.ipcRenderer.on('channel-name', args) sample below
const myEventHandler = () => {
window.electron.ipcRenderer.on('channel-name', (event, data) => {
console.log('data, event', data, event);
});
};
window.electron, here electron is the name given in preload.ts file. contextBridge.exposeInMainWorld('electron', {...})

Related

Vue 3's Provide / Inject using the Options API

I've been trying to follow the documentation for the API on the Vue 3 website which says to use app.provide('keyName',variable) inside your main.js file like so:
import App from './App.vue'
import { createApp } from 'vue'
import axios from 'axios'
const app = createApp(App)
app.provide('axios', axios)
app.use('Vue')
app.mount('#app')
Then inject and use it in your child component like so:
export default {
inject: ['axios'],
...
createUser (data) {
return this.axios.post('/users', data)
}
}
However doing so just gives me this error in my console:
Uncaught TypeError: Cannot read properties of undefined (reading 'post')
Is there anything I'm missing? I didn't see any about an import unless you're using the Composition API. Can provide / inject be called from within a .js file? I would expect so as long as its within a export default {} statement
Ive tried following the API to a "T" but it simply refuses to work for me. Also tried searching the web for solutions but everything I've found says what I'm doing should be working just fine.
It works, see the playground.
But is not absolutely necessary, since with the browser library version axios is globally defined and could be accessed also without inject
You could also save yourself some time with the vue-axios plugin.
Example
const { createApp } = Vue;
const myComponent = {
inject: ['axios'],
created() {
this.axios.get('/')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
},
template: '<div>My Component</div>'
}
const App = {
components: {
myComponent
}
}
const app = createApp(App)
app.provide('axios', axios)
app.mount('#app')
<div id="app">
<my-component></my-component>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://unpkg.com/axios#1.3.1/dist/axios.min.js"></script>

React-Native Module in Swift with async function throws is not a recognized Objective-C method

I am trying to export a swift module according to react-native's documentation but I run into the following error when I try to call this function in my javascript file. I believe the issue has to do with calling an async method in my swift code. So I'm not sure how to export and call an async method from my swift file when creating a native module in react-native.
Here is the AppleMusicAuth.swift file that contains the async method I'm trying to export
//
// AppleMusicAuth.swift
//
//
//
import Foundation
import MusicKit
#available(iOS 15.0, *)
#objc(AppleMusicAuth)
class AppleMusicAuth: NSObject {
#objc
func getAuthStatus() async -> Void {
let response = await MusicAuthorization.request()
print(response)
}
}
Along with my AppleMusicAuth.m file that makes use of RCT_EXTERN_METHOD method for exporting the module to my JS application
//
// AppleMusicAuth.m
//
//
//
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(AppleMusicAuth, NSObject)
RCT_EXTERN_METHOD(getAuthStatus)
#end
And the Javascript files that import and call the module and method for use
import { NativeModules } from 'react-native';
const { AppleMusicAuth } = NativeModules;
export default AppleMusicAuth;
import React from 'react';
import {Button} from 'react-native';
import AppleMusicAuth from '../nativeModules/AppleMusicAuth';
type Props = {};
const App: React.FC<Props> = () => {
return (
<Button
onPress={() => {
AppleMusicAuth.getAuthStatus();
}}
title="Button"
color="#841584"
/>
);
};
export default App;
Edit: my recommendation as of 10.01.2023 is to use expo-modules-core as it has support for this use-case.
See: https://docs.expo.dev/modules/get-started/
Example: https://github.com/expo/expo/pull/20645
Original Answer
I just ran into this, here is my workaround.
Instead of exporting an async function to React Native/Objective-C, we can export a regular function which then calls the async one.
To call an async function from a normal function in Swift we can use Task
To send the result back to React Native we can use events.
func myAsyncFunction(_ data: NSDictionary) async -> Void {
// Processing...
self.sendEvent(withName: "MyEvent", body: "The result")
}
#objc
func myExportedFunction(_ data: NSDictionary) -> Void {
Task {
await myAsyncFunction(data)
}
}

Access to main Vue instance in App.vue script

In Vue2, I'm trying to set up an axios interceptor in my App.vue file to trap any responses that come back 401 from my API so I can redirect the user to the Vue route /sign-in. My code works, but I rely on storing the Vue instance created in main.js in window.appvue.
In main.js:
window.appvue = new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
In App.vue:
<script>
import...
export default { ... }
export const $axios = axios.create();
$axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response.status === 401) {
window.appvue.$router.push("/sign-in");
} else {
return Promise.reject(error);
}
}
);
<script>
I have tried importing $axios from App.vue in main.js and moving the $axios.interceptors.response.use(...) code to that file, but then the interceptor never runs when I have a page where an $axios.get() returns 401.
Is there a way to accomplish this without storing the main Vue instance in window as a global? Or should I just go with what's working and call it a day?
P.S.
I was not aware of the existence of $root, when I asked this question, and I have not tried a version where the code in App.vue uses $root instead of relying on the window.appvue global, but when it comes to accessing the main/root instance of Vue from main.js, $root is definitely preferable to a global.
I managed to avoid using the global by isolating the axios wrapper into its own module/file. Then my .vue files that need axios can import from that instead of from axios directly
In {root}/src/axios.js:
import axios from "axios";
import router from "#/router";
const $axios = axios.create();
$axios.interceptors.response.use(
[stuff that uses router]
);
export default $axios;
Then in my files that need to call to the API:
import $axios from "#/axios.js";
and use $axios instead of axios to make the API call.
inside your main.js try something like this
import router from './router'
import axios from 'axios'
Vue.prototype.axios.interceptors.reponse.use(
res => {
// res stuff here
},
err => {
// error stuff here
if (err.response.status === 401) {
router.push("/sign-in");
} else {
return Promise.reject(err);
}
}
)

Vue 3 Event Bus with Composition API

I have setup mitt and trying to dispatch event to another component but I am having hard time because in the setup() method it doesn't have this for accessing app instance.
Here is what I tried:
import App from './App.vue'
const el = document.getElementById('app')
import mitt from 'mitt';
const emitter = mitt();
const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);
And in the component, I want to dispatch an event
export default {
setup() {
function toggleSidebar() {
this.emitter.emit('toggle-sidebar');
console.log(this); // binds to setup(), not the vue instance.
}
}
}
As this doesn't exist, I can't access the .emitter. What am I missing? How to use officially suggested mitt in Vue 3 composition api?
By the way if I use the v2 syntax, I can access this.emitter. But I am curious about Composition API way
export default {
mounted() {
console.log(this.emitter); // works
}
}
To use an event bus in Vue 3 Composition API, use Vue 3's new provide api in main.js, and then inject in any component:
1. Install mitt:
npm install mitt
2. Provide:
main.js
import { createApp } from 'vue';
import App from './App.vue';
import mitt from 'mitt'; // Import mitt
const emitter = mitt(); // Initialize mitt
const app = createApp(App);
app.provide('emitter', emitter); // ✅ Provide as `emitter`
app.mount('#app');
3. Inject
3a. Any Component - Emit an event
import { inject } from 'vue'
export default {
setup() {
const emitter = inject('emitter'); // Inject `emitter`
const mymethod = () => {
emitter.emit('myevent', 100);
};
return {
mymethod
}
}
}
Call mymethod from a button click or something.
3b. Any Component - Listen for the event
import { inject } from 'vue'
export default {
setup() {
const emitter = inject('emitter'); // Inject `emitter`
emitter.on('myevent', (value) => { // *Listen* for event
console.log('myevent received!', `value: ${value}`);
});
},
}
Console
myevent received! value: 100
You may be able to use getCurrentInstance to get the global property
component:
import { getCurrentInstance } from 'vue';
export default {
setup() {
// get current instance
const internalInstance = getCurrentInstance();
// get the emitter from the instance
const emitter = internalInstance.appContext.config.globalProperties.emitter;
}
}
So far I have used this code to make the "emitter" available.
//main.ts
import mitt from 'mitt'
const emitter = mitt()
export default emitter
And then inside the components I use
import emitter from '#/main';
This worked so far in Vue2 and Vue3 - at least with the options API.
I have to admit though that I currently run into some trouble with the new vite server and the hot module reload (hmr).
Is this style suboptimal in any way?

Load function from external script using #loadable/component in React

I have a JSON file with several filepaths to scripts that I want to be able to load dynamically into my React app, to build each component based on specifications that are in the metadata. Currently I have the metadata in my app as a Metadata data object.
metadata.json:
{
"component1": { "script": "./createFirstLayer.js" },
"component2": { "script": "./createSecondLayer.js" }
}
Each script exports a function that I want to be able to use to construct the component. For troubleshooting purposes, it currently only returns a simple message.
function createFirstLayer(name) {
return name + " loaded!";
}
export default createFirstLayer;
I did some research and identified the #loadable/component package. Using this package as import loadable from "#loadable/component";, I attempted to load my script into App.js like this:
async componentDidMount() {
Object.keys(Metadata).forEach(function(name) {
console.log(Metadata[name].script);
var createLayer = loadable(() => import(Metadata[name].script));
var message = createLayer(name);
console.log(message);
});
}
Everything I have tried throws the TypeError createLayer is not a function. How can I get the function loaded?
I have also attempted the lazy method.
I have recreated a working demo of my problem here.
EDIT: I have tried to put this at the top of my app
const scripts = {};
Object.keys(Metadata).forEach(async function(name) {
import(Metadata[name].script).then((cb) => scripts[name] = cb);
});
This causes the TypeError Unhandled Rejection (Error): Cannot find module './createFirstLayer.js'. (anonymous function)
src/components lazy /^.*$/ groupOptions: {} namespace object:66
I have also attempted
const scripts = {};
Object.keys(Metadata).forEach(async function(name) {
React.lazy(() => import(Metadata[name].script).then((cb) => scripts[name] = cb));
});
My goal is to be able to call the appropriate function to create particular layer, and match them up in the metadata.
You don't need #loadable/component for two reasons.
You can accomplish your goal with dynamic imports
'#loadable/component' returns a React Component object, not your function.
To use dynamic imports simply parse your JSON the way you were, but push the call to the import's default function into state. Then all you have to do is render the "layers" from within the state.
Like this:
import React, { Component } from "react";
import Metadata from "./metadata.json";
class App extends Component {
constructor(props) {
super(props);
this.state = { messages: [] };
}
async componentDidMount() {
Object.keys(Metadata).forEach(name=> import(`${Metadata[name].script}`).then(cb =>
this.setState((state, props) => ({ messages: [...state.messages, cb.default(cb.default.name)] }))));
}
render() {
return (
<div className="App">
{this.state.messages.map((m, idx) => (
<h1 key={idx}>{m}</h1>
))}
</div>
);
}
}
export default App;
Here is the working example

Categories

Resources