AppBridgeError INVALID_CONFIG: host must be provided - shopify-app

I am facing an error while trying to complete the official shopify tutorial about developing shopify app.
I have been following the tutorial step by step but even then ran in to problem where the error is thrown that my config file is invalid as it does not contain the host.
My _app.js file code is as follow:
import React from "react";
import App from "next/app";
import Head from "next/head";
import { AppProvider, Frame } from "#shopify/polaris";
import "#shopify/polaris/dist/styles.css";
import translations from "#shopify/polaris/locales/en.json";
import { Provider } from "#shopify/app-bridge-react";
import ClientRouter from "../components/ClientRouter";
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
const config = {
apiKey: API_KEY,
shopOrigin,
forceRedirect: true,
};
console.log(config);
return (
<React.Fragment>
<Head>
<title>Sample App</title>
<meta charSet="utf-8" />
</Head>
<Provider config={config}>
<ClientRouter />
<AppProvider i18n={translations}>
<Frame>
<Component {...pageProps} />
</Frame>
</AppProvider>
</Provider>
</React.Fragment>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
the console for the config file gives the right shopOrigin.
Ill be thankful if anyone helps

At the moment use the App Bridge version 2 is required like App review team points out:
Ensure that your app is on Shopify App Bridge version 2.0 to embed
your app within merchants' admin...
So, what I did was persist the shop name (Ex: toys.myshopify.com) in my database, encode to base64 and send like "host" query param when is required.
const host = Buffer.from(`${shop}/admin`).toString('base64');
ctx.redirect(`${process.env.HOST}?shop=${shop}&host=${host}`);
Note how i added "/admin" after the shop values, this is an important step, otherwise you get a 404 error (Page not found).

Related

Is it possible to use the layout in the _app.jsx component with next-i18next?

To create a site, I use nextjs, when creating pages, I took the general layout with the header and footer into a separate hoc component and wrapped the page components in the file with it _app.jsx:
function App({ Component, ...rest }) {
const { store, props } = wrapper.useWrappedStore(rest)
return (
<Provider store={store}>
<Layout>
<Component {...props.pageProps} />
</Layout>
</Provider>
)
}
Everything worked fine until localization became a problem, after using the next-18next library for translations and adding serverSideTranslations, two errors began to appear on each page:
react-i18next:: You will need to pass in an i18next instance by using initReactI18next
frontend-node_1 | TypeError: Cannot read properties of undefined (reading 'label')
frontend-node_1 | at DropdownSwitcher (webpack-internal:///./src/components/header/translation/DropdownSwitcher.jsx:45:36)
frontend-node_1 | at renderWithHooks (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5658:16)
frontend-node_1 | at renderIndeterminateComponent (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5731:15)
frontend-node_1 | at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5946:7)
frontend-node_1 | at renderMemo (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5868:3)
frontend-node_1 | at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6011:11)
frontend-node_1 | at renderNodeDestructiveImpl (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6104:11)
frontend-node_1 | at renderNodeDestructive (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6076:14)
frontend-node_1 | at renderNode (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:6259:12)
frontend-node_1 | at renderHostElement (/app/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5642:3)
The error with "label" occurs because the i18n object is empty on the server:
const DropdownSwitcher = () => {
const { i18n } = useTranslation()
const currentLanguage = useMemo(() => { // language as undefined
return LANGUAGES.find((item) => item.language === i18n.language)
}, [i18n.language])
....
But everything is fine on the client and there are no errors. What could be the reason and how to fix it, since the App itself from the _app.jsx file is wrapped in appWithTranslation from next-i18next.
Therefore, two questions arise, how to fix react-i18next:: You will need to pass in an i18next instance by using initReactI18next and why there is no i18n object on the server?
I moved the layout to the level of the page itself, removing it from _app.js, but for some reason, then something, useEffect() is repeated in the header, although the header component has not changed in any way and bringing the layout to the level of _app.jsx fixes it
If there is not enough information or you need a visual example, I will try to create a small program that demonstrates this with open source. Please write in a comment.
I solved my problem, but I forgot to provide an answer here, but I noticed that someone also has this problem, so I will try to help people who come across this post, although it is relevant only for nextjs version 12, since with the appearance of version 14, the structure there has improved a lot with as I think there should be no more questions like mine.
1. Rendering the layout
In the official doc, there is a whole section that describes how to correctly divide the layout so that it works according to the SPA type.
pages/index.jsx
// pages/index.jsx
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** Your content */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
pages/_app.js
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
This component method approach is much better than using its direction in _app.jsx because you can extend or replace them and not make a crude monolith, example how I used it:
// pages/ingex.jsx
function HomePage() {
return (
<HomeLayout>
<Main />
</HomeLayout>
)
}
HomePage.getLayout = (page) => <MainLayout>{page}</MainLayout>
// pages/about-us.jsx
const AboutUsPage = () => {
return (
<>
<HomeLayout>
<AboutUs />
</HomeLayout>
</>
)
}
AboutUsPage.getLayout = (page) => (
<MainLayout withNav>
<LayoutContext.Consumer>
{({ device }) => device.isMobile && <NavigationMobile />}
</LayoutContext.Consumer>
{page}
</MainLayout>
)
With this approach, react still works like a spa and a similar page to about-us, which will also have NavigationMobile, will simply compare it.
2. Error with next-i18next
The whole point was that the next-i18next library was configured incorrectly in the first place (more precisely, it needed to be corrected). In order to configure everything correctly, I had to do the following:
- Move the folder with translation files to the public folder. This is necessary so that the library config, which we will configure a little below, can see the translation files and interact with them
- Configure next-i18next.config.js to work with the client. Here is an example setup with some comments. And also a link to the documentation, and some other resources I found while setting up.
next-i18next.config.js
const path = require('path')
const LANGUAGES = ['en', 'pl', 'uk']
const DEFAULT_LANGUAGE = 'en'
// if it is the server, then the full path, if the client, then the relative path.
const localePath =
typeof window === 'undefined' ? path.resolve('public', 'translation') : '/public/translation'
module.exports = {
i18n: {
defaultLocale: DEFAULT_LANGUAGE,
locales: LANGUAGES,
fallbackLng: LANGUAGES,
nsSeparator: '::',
keySeparator: '::',
// How to use libraries for i18next like LanguageDetector
use: [require('i18next-intervalplural-postprocessor')],
serializeConfig: false,
},
localePath: localePath,
}
- Configure next-i18next in the _app.jsx file. Here everything is as described in the documentation.
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../../next-i18next.config'
function App({ Component, ...rest }) {
const { store, props } = wrapper.useWrappedStore(rest)
const getLayout = Component.getLayout || ((page) => page)
//WARNING!!! You don't have to have your own i18next initialization like i18next.use(LanguageDetector).use(intervalPlural).init({ detection: options }) this is all done by the next-i18next library
return (
<Provider store={store}>
<AppHOC>{getLayout(<Component {...props.pageProps} />)}</AppHOC>
</Provider>
)
}
export default appWithTranslation(App, nextI18NextConfig)
- You need to pass the config when calling the serverSideTranslations function. To make your life easier, it is better to transfer the implementation of this function to another file, here is an example of how I did it:
// utils/serverSideTranslations.js
import { serverSideTranslations as baseServerSideTranslations } from 'next-i18next/serverSideTranslations'
import { dt } from '../../constants/defaultTranslate'
import { DEFAULT_LANGUAGE } from '../../constants/languages'
import nextI18NextConfig from '../../../next-i18next.config.js'
const serverSideTranslations = async (locale, domains = []) => {
return await baseServerSideTranslations(locale, [...dt, ...domains], nextI18NextConfig, [
DEFAULT_LANGUAGE,
])
}
export default serverSideTranslations
- And finally, use this function on the pages.
import MainLayout from '../components/layouts/MainLayout'
import serverSideTranslations from '../utils/serverSideTranslations'
import HomeLayout from '../components/home/HomeLayout'
import Main from '../components/home/main/Main'
function HomePage() {
return (
<HomeLayout>
<Main />
</HomeLayout>
)
}
HomePage.getLayout = (page) => <MainLayout>{page}</MainLayout>
export const getServerSideProps = async ({ locale }) => {
// Wrapping in Promis.all is not necessary, I use it simply so that if there are any other asynchronous operations, then not to use them through await and not to block each other's work
const [translations] = await Promise.all([
serverSideTranslations(locale, ['home']),
])
return {
props: {
...translations,
},
}
}
export default HomePage
I hope this helped someone, if you have any comments, write in the comments

Open Telemetry for react and vanilla JS projects

Can someone help me understand if there is a way to configure open Telemetry on the client side for react and vanilla JS projects all I want to do is to console the traces of fetch call that are being made from the browser.
Most of the documentation I see is only for nodejs. Pls pinpoint a documentation if there are any?
The documentation gives a common guide for Javascript. What you do for you React would be same as what you do for Node.js or even simple JS scripts.
Just follow the documentation. Create and export a tracer:
import { ZoneContextManager } from '#opentelemetry/context-zone';
import { registerInstrumentations } from '#opentelemetry/instrumentation';
import { DocumentLoadInstrumentation } from '#opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '#opentelemetry/instrumentation-fetch';
import { UserInteractionInstrumentation } from '#opentelemetry/instrumentation-user-interaction';
import { XMLHttpRequestInstrumentation } from '#opentelemetry/instrumentation-xml-http-request';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '#opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '#opentelemetry/sdk-trace-web';
const setupTracer = () => {
const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.register({
// Changing default contextManager to use ZoneContextManager - supports asynchronous operations - optional
contextManager: new ZoneContextManager(),
});
// Registering instrumentations
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new UserInteractionInstrumentation(),
new XMLHttpRequestInstrumentation(),
new FetchInstrumentation()
],
});
}
export default setupTracer;
Import the tracer like this in your app's entry point (usually index.js):
setupTracer();
ReactDOM.render(<App />, document.getElementById('root'));

PersistGate stuck on loading when query parameter present in URL

I'm using the example with-redux-persist repo paired with next.js found here: https://github.com/vercel/next.js/tree/canary/examples/with-redux-persist
When using a custom loading component, adding a query parameter to the URL, ie ?test=123, causes the page to hang on loading indefinitely. There are no errors reported in console and the redux state info looks clean.
Here is my _app.js I'm using to test this:
import { useStore } from '../store'
import { Provider } from 'react-redux'
import { persistStore } from 'redux-persist'
import { PersistGate } from 'redux-persist/integration/react'
export default function App({ Component, pageProps }) {
const store = useStore(pageProps.initialReduxState)
const persistor = persistStore(store)
return (
<Provider store={store}>
<PersistGate loading={<div>Loading</div>} persistor={persistor}>
<Component {...pageProps} />
</PersistGate>
</Provider>
)
}
You can reproduce it by cloning the above repo, modifying _app.js to look like above, starting the server with yarn dev and then finally visiting http://localhost:3000/?test=123.
Any help or insight into why this is occurring is appreciated!

No provider for ConnectionBackend - (angular2 and springBoot)

i'm trying to connect my spring boot project as my back-end using angular 2 as front-end.
based on web-service my back-end provide an API which present on JSON, then from here i'm building my front-end then 'm try to consume Restful service.
eventually, no component in running, a blank page displayed with three error on my web browser said:
error 1:
ERROR Error: No provider for ConnectionBackend!
at injectionError (core.es5.js:1169)
at noProviderError (core.es5.js:1207)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._throwOrNull (core.es5.js:2649)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKeyDefault (core.es5.js:2688)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKey (core.es5.js:2620)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_.get (core.es5.js:2489)
at resolveNgModuleDep (core.es5.js:9475)
at NgModuleRef_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.NgModuleRef_.get (core.es5.js:10557)
at resolveDep (core.es5.js:11060)
at createClass (core.es5.js:10916)
error 2:
AppComponent_Host.html:1 ERROR CONTEXT DebugContext_ {view: Object, nodeIndex: 1, nodeDef: Object, elDef: Object, elView: Object}
error 3:
Unhandled Promise rejection: No provider for ConnectionBackend! ; Zone: <root> ; Task: Promise.then ; Value: Error: No provider for ConnectionBackend!
at injectionError (core.es5.js:1169)
at noProviderError (core.es5.js:1207)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._throwOrNull (core.es5.js:2649)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKeyDefault (core.es5.js:2688)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKey (core.es5.js:2620)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_.get (core.es5.js:2489)
at resolveNgModuleDep (core.es5.js:9475)
at NgModuleRef_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.NgModuleRef_.get (core.es5.js:10557)
at resolveDep (core.es5.js:11060)
at createClass (core.es5.js:10916) Error: No provider for ConnectionBackend!
at injectionError (http://localhost:4200/vendor.bundle.js:33285:90)
at noProviderError (http://localhost:4200/vendor.bundle.js:33323:12)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._throwOrNull (http://localhost:4200/vendor.bundle.js:34765:19)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKeyDefault (http://localhost:4200/vendor.bundle.js:34804:25)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_._getByKey (http://localhost:4200/vendor.bundle.js:34736:25)
at ReflectiveInjector_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.ReflectiveInjector_.get (http://localhost:4200/vendor.bundle.js:34605:21)
at resolveNgModuleDep (http://localhost:4200/vendor.bundle.js:41591:25)
at NgModuleRef_.webpackJsonp../node_modules/#angular/core/#angular/core.es5.js.NgModuleRef_.get (http://localhost:4200/vendor.bundle.js:42673:16)
at resolveDep (http://localhost:4200/vendor.bundle.js:43176:45)
at createClass (http://localhost:4200/vendor.bundle.js:43032:35)
anyway guessing my back-end just gonna give as only one list in the other hand the angular2 project just gonna list all att names from the list provided by spring boot.
here my Angular code:
app.component.ts
import { NgFor } from '#angular/common';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: `<h1>Consumming a restful Webservice using angular 2</h1>
<ul>
<li #ngFor="item of personsLst">
{{item.id}}
</li>
</ul>`,
providers: [Http]
})
export class AppComponent {
personsLst: Array<string> = [];
theDataSource: Observable<string>;
constructor(private http: Http) {
this.theDataSource = this.http.get('/persons/dolist')
.map(res => res.json());
}
ngOnInit() {
// get the data from the rest service
this.theDataSource.subscribe(
data => {
if (Array.isArray(data)) {
this.personsLst = data;
}else {
this.personsLst.push(data);
}
},
err =>
console.log('can not get the persons list. Error code: %s, URL: %s', err.status, err.url),
() => console.log('Person(s) retrieved')
);
}
}
Note: i'm using the angular command line interface in this project!!

JS/(X) import: to import React components?

I want to import a React component from a jsx file in a template and render it in the template with ReactDOM. Later in production I would only want to ship react and all the dependencies of the component only when a site is loaded that has that component.
I have created a React component like this:
editor.jsx
import * as React from "react";
import {Editor} from "draft-js-plugins-editor";
const plugins = [];
export class EditorComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
}
onChange(editorState) {
this.setState({
editorState,
});
}
render() {
return (<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
/>);
}
}
http://www.phoenixframework.org/docs/static-assets suggests the require syntax for accessing module exports. So I added the following to my template <script>const editor = require("web/static/js/editor").EditorComponent</script>. This does not work though, because the browser cannot interpret require (or brunch does not pick it up).
I configured brunch like so:
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/web\/static\/vendor/],
presets: ["es2015","react"]
}
},
modules: {
autoRequire: {
"js/app.js": ["web/static/js/app"],
"js/editor.jsx": ["web/static/js/editor"]
}
},
I am a bit lost here. How can this be done?
One idea that pops to mind is to create a JS file and import it in the template you want with a <script> tag. In the same template create an empty <div id=editor>. Then, in the JS file import React and ReactDOM and the component you want and use something like this:
ReactDOM.render(
<Editor/>,
document.getElementById("editor")
)
However, I'm not sure I understand your problem correctly.

Resources