Async function support in Mithril.js for webpack dynamic imports? - mithril.js

I'm trying to figure out how to use dynamic import in webpack with mithril. To do that elegantly, I think I'll need to use an async function somewhere along the line. Right now this is how I have used the async function:
import m from 'mithril'
let App = async () => {
let { Component } = await import('./components.js')
return {
view () {
return m(Component)
}
}
}
App().then(app => m.mount(document.body, app))
Ideally, I want to use it like this:
import m from 'mithril'
let App = {
async view () {
let { Component } = await import('./components.js')
return m(Component)
}
}
}
m.mount(document.body, App)
Is there something I've been missing from the documentation to acheive what I'd like to do? I've tried to look at every mention of promise, but it's possible that I've missed this.
Any help would be appreciated.

One way that should work is this:
async function main() {
const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main();
(async () => {
const myModule = await import('./myModule.js');
})();
For further information follow the link below.
ES proposal: import() – dynamically importing ES modules

Try the following, which provides a simple component named DynamicComponent which can be used anywhere and with children:
App.js
import m from 'mithril'
import { DynamicComponent } from './DynamicComponent'
const App = {
view() {
return m( DynamicComponent, {
component: 'OtherComponent'
}, 'Hello world' ),
}
}
}
m.mount(document.body, App)
OtherComponent.js
import m from 'mithril'
export function OtherComponent() { return {
view({ children }) { return m( 'div', children )}
}}
DynamicComponent.js
import { hooks } from '/hooks'
export function DynamicComponent() { return {
...hooks,
attrs: null,
component: null,
view({ children }) { return (
// Await module resolution and process attributes.
// Use '&&' as a shortcut to only continue
// once 'this.component' isn't null.
// Pass a clone of attrs to the loaded component.
this.component && m( this.component.default, this.attrs, children )
)}
}}
hooks.js
async function oninit({ attrs }) {
// Preload -> Load immediately, in parallel
// Prefetch -> Load when browser is idle (Can be less responsive)
// See more: https://webpack.js.org/guides/code-splitting/#prefetching-preloading-modules
// Dynamically import component and append '.js' (Don't include '.js' in your imports).
if ( attrs.loadType = 'prefetch' ) {
// Lazy load
this.component = await import( /* webpackPrefetch: true */ `
${ attrs.component }.js`
)
} else {
// Immediate load
this.component = await import( /* webpackPreload: true */ `
${ attrs.component }.js`
)
}
/*
Process and pass along attributes
This clones the attributes to prevent any changes from affecting
the original attributes.
You can save memory if it becomes a problem by directly
assigning `v.attrs` to `newAttrs`, but you lose this immutability.
*/
const newAttrs = { ...v.attrs }
// Remove attributes used in `DynamicComponent`
delete newAttrs.component
delete newAttrs.loadType
// Assign to component
this.attrs = newAttrs
m.redraw()
}
export const hooks = {
oninit,
}

Related

Using XState in Nuxt 3 with asynchronous functions

I am using XState as a state manager for a website I build in Nuxt 3.
Upon loading some states I am using some asynchronous functions outside of the state manager. This looks something like this:
import { createMachine, assign } from "xstate"
// async function
async function fetchData() {
const result = await otherThings()
return result
}
export const myMachine = createMachine({
id : 'machine',
initial: 'loading',
states: {
loading: {
invoke: {
src: async () =>
{
const result = await fetchData()
return new Promise((resolve, reject) => {
if(account != undefined){
resolve('account connected')
}else {
reject('no account connected')
}
})
},
onDone: [ target: 'otherState' ],
onError: [ target: 'loading' ]
}
}
// more stuff ...
}
})
I want to use this state machine over multiple components in Nuxt 3. So I declared it in the index page and then passed the state to the other components to work with it. Like this:
<template>
<OtherStuff :state="state" :send="send"/>
</template>
<script>
import { myMachine } from './states'
import { useMachine } from "#xstate/vue"
export default {
setup(){
const { state, send } = useMachine(myMachine)
return {state, send}
}
}
</script>
And this worked fine in the beginning. But now that I have added asynchronous functions I ran into the following problem. The states in the different components get out of sync. While they are progressing as intended in the index page (going from 'loading' to 'otherState') they just get stuck in 'loading' in the other component. And not in a loop, they simply do not progress.
How can I make sure that the states are synced in all my components?

React createContext running mutation function not working correctly

I am having a problem i can't seem to understand why is happening since i have the same example working in codesandbox, but in my app it shows a different behavior. In my app i can see the context from the consumer both the bool and the function, but when i run the function it runs the empty function "setUpdate: () => {}" instead of running the "updBool()" in UpdateDataProvider.js file. Anyone know why this behaviour happens.
(component.js is not my actual file just a short example of how im using the context)
UpdateDataProvider.js
export const UpdateDataContext = createContext({
update: false,
setUpdate: () => {},
});
export function UpdateDataContexProvider({ children }) {
function updBool(bool) {
setU({ ...u, update: bool });
}
const [u, setU] = useState({ update: false, setUpdate: updBool });
return (
<UpdateDataContext.Provider value={u}>
{children}
</UpdateDataContext.Provider>
);
}
useUpdateData.js
import { useContext } from 'react';
import { UpdateDataContext } from '../../context/updateDataContext';
export function useUpdateDataContext() {
return useContext(UpdateDataContext);
}
component.js
import { UpdateDataContexProvider } from '../../context/updateDataContext';
import { useUpdateDataContext } from '../../hooks/exports';
useEffect(() => {
// loging the context shows me update bool and setUpdate function
console.log(context)
// Running the function will run the empty function in createContext
// in UpdateDataProvider.
context.setUpdate(true)
}, [])
export default Home = () => {
const context = useUpdateDataContext()
return (
<UpdateDataContexProvider>
<Other />
</UpdateDataContexProvider>
)
}
Don't mind my question, the mistake was that i was trying to run the function in useEffect in the home component but not the childs

Returning Apollo useQuery result from inside a function in Vue 3 composition api

I'm having some issues finding a clean way of returning results from inside a method to my template using Apollo v4 and Vue 3 composition API.
Here's my component:
export default {
components: {
AssetCreationForm,
MainLayout,
HeaderLinks,
LoadingButton,
DialogModal
},
setup() {
const showNewAssetModal = ref(false);
const onSubmitAsset = (asset) => {
// how do I access result outside the handler function
const { result } = useQuery(gql`
query getAssets {
assets {
id
name
symbol
slug
logo
}
}
`)
};
}
return {
showNewAssetModal,
onSubmitAsset,
}
},
}
The onSubmitAsset is called when user clicks on a button on the page.
How do I return useQuery result from the setup function to be able to access it in the template? (I don't want to copy the value)
You can move the useQuery() outside of the submit method, as shown in the docs. And if you'd like to defer the query fetching until the submit method is called, you can disable the auto-start by passing enabled:false as an option (3rd argument of useQuery):
export default {
setup() {
const fetchEnabled = ref(false)
const { result } = useQuery(gql`...`, null, { enabled: fetchEnabled })
const onSubmitAsset = (asset) => {
fetchEnabled.value = true
}
return { result, onSubmitAsset }
}
}
demo

How to use react-i18next inside BASIC function (not component)?

I know that react-i18next work in every component: functional (with useTranslation) and class component (with withTranslation()) BUT I can't use translation inside a basic function like this:
const not_a_component = () => {
const { t } = useTranslation();
return t('translation')
};
const translate = not_a_component();
ERROR HOOKS !
Thanks !
You could just use i18next library for translation using javascript.
react-i18next is just a wrapper library on top of i18next.
Below is an example if you are already using react-i18next and it is configured.
import i18next from "i18next";
const not_a_component = () => {
const result = i18next.t("key");
console.log(result);
return result;
};
export default not_a_component;
If you opt to use only i18nextthen you could simply get t function.
It all depends upon your requirement.
import i18next from 'i18next';
i18next.init({
lng: 'en',
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
}, function(err, t) {
// You get the `t` function here.
document.getElementById('output').innerHTML = i18next.t('key');
});
Hope that helps!!!
Alternatively, you can pass t as an additional parameter:
const not_a_component = (t) => {
return t('translation')
};
// Within a component
const { t } = useTranslation()
not_a_component(t)

redux-saga: eventChannel and listener which react callback return

In react-native backhandler listener react to callback function and act appropriately.
I need to read my store and depending on it, return true or false.
But I cant use select effect in normal function and I cant affect listener callback function from "watchBackButton" function.
export function* backButtonListen() {
return eventChannel(emitter => {
const backHandlerListener = BackHandler.addEventListener(
"hardwareBackPress",
() => {
emitter("back pressed");
}
);
return () => {
backHandlerListener.remove();
};
});
}
export function* watchBackButton() {
const chan = yield call(backButtonListen);
try {
while (true) {
let back = yield take(chan);
}
}
Since event channels are not bidirectional, I don't think there is a way to get some current state from saga to event channel using the select effect.
However, it is possible to access the store directly. There are multiple ways to get the store instance to the event channel. See my other answer here.
Using e.g. the context method you could do something like this:
// redux.js
...
const store = createStore(...);
sagaMiddleware.runSaga(rootSaga, {store});
// root-saga.js
export default function * rootSaga(context) {
yield setContext(context);
yield fork(watchBackButton);
}
// watch-back-button.js
export function* backButtonListen() {
const store = yield getContext('store');
return eventChannel(emitter => {
const backHandlerListener = BackHandler.addEventListener(
"hardwareBackPress",
() => {
emitter("back pressed");
return store.getState().foo === 'bar';
}
);
return () => {
backHandlerListener.remove();
};
});
}
export function* watchBackButton() {
const chan = yield call(backButtonListen);
try {
while (true) {
let back = yield take(chan);
}
}