I want to show the root component of the App based on config variable from react-native-config
I want to achieve something like this. I have a IS_STORYBOOK variable in the .env file, i want to setup my environment so i can just set the value from config and switch to main application and storyboard mode in my react-native application.
By doing this way.. i am getting this error bundling failed: SyntaxError: D:\Projects\React\React-Native Sample app\MobileApp\App.js: 'import' and 'export' may only appear at the top level (62:1)
//App.js
import React from 'react'; // eslint-disable-line
import { Provider } from 'react-redux';
import { pushNotifications } from './src/global/services';
import configureStore from './src/store/configureStore';
import {StackNavigator, createDrawerNavigator } from 'react-navigation'
import { generateStack } from './src/navigation/routesBuilder'
import Drawer from './src/components/drawer/container'
import {items} from './src/components/drawer/draweritems';
import DrawerIcon from './src/components/navIcons/drawerIcon'
import {data} from './src/global/data'
import {scale} from './src/utils/scale'
import StoryBook from './storybook';
import Config from 'react-native-config'
const store = configureStore();
pushNotifications.configure();
data.populateData();
const drawerRoutes = {"app.home":{
screen:generateStack("app.home", "Home", true,true)
}}
for(var i=0; i<items.length; i++){
drawerRoutes[items[i].navigateTo] = {
screen : generateStack(items[i].navigateTo, items[i].title, true, true),
}
}
const RootStack = StackNavigator({
Splash: {
screen: generateStack('app.splash', '', false, false),
navigationOptions:{
header: null
}
},
Auth: {
screen : generateStack('auth.login', '', false, false),
navigationOptions:{
header: null
}
},
Home:{
screen : createDrawerNavigator({
...drawerRoutes
},
{
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
drawerPosition:'left',
drawerWidth:scale(300),
drawerIcon : (<DrawerIcon />),
contentComponent: (props) => <Drawer {...props}/>
}),
}
}, {
headerMode:
'none'
});
export default class App extends React.Component {
render() {
if(Config.IS_STORYBOOK){
return <StoryBoard />
} else {
return(
<Provider store={store}>
<RootStack />
</Provider>
)
}
}
}
//storybook.js
import { AppRegistry } from "react-native";
import { getStorybookUI, configure } from "#storybook/react-native";
import { loadStories } from "./storyLoader";
configure(() => {
loadStories();
}, module);
const StorybookUI = getStorybookUI({
port: 7007,
host: "localhost",
onDeviceUI: true,
resetStorybook: true
});
AppRegistry.registerComponent("sampleproject", () => StorybookUI);
export { StorybookUI as default };
// .env
IS_STORYBOOK=false
React components are bundled during build-time and not during run-time.
You cannot conditionally export or import your components. Instead you should conditionally render stuff in your components.
Something like this might work for you
import StoryBook from './storybook';
export default class App extends React.Component {
render() {
if(!Config.IS_STORYBOOK){
return(
<Provider store={store}>
<RootStack />
</Provider>
)
} else {
return <StoryBook />
}
}
}
Related
Am trying to load google font to Nextjs project but I'm getting the error below. In nextjs12 It works okay but I have to use the link for the fonts in the head. This error occurs also using #next/font/local.
I've struggled with this and any help is appreciated.
Am using the official docs process for adding global fonts. Am on Nextjs13.
My _app.tsx code is:
import { useApollo } from "#api/apollo/apolloclient";
import { ApolloProvider } from "#apollo/client";
import { CacheProvider, EmotionCache } from "#emotion/react";
import { Ubuntu } from "#next/font/google";
import "focus-visible/dist/focus-visible";
import { DefaultSeo } from "next-seo";
import { AppProps } from "next/app";
import { ChakraProvider, ColorModeScript } from "#chakra-ui/react";
import theme from "#definitions/chakra/theme";
import { ThemeColorProvider } from "#definitions/context/theme";
import createEmotionCache from "#definitions/utils/createEmotionCache";
import Layout from "#layouts/default";
import "#styles/app.css";
import "#styles/global.scss";
import SEO from "../../next-seo.config";
import { useEffect, useState } from "react";
type ComponentWithPageLayout = AppProps & {
Component: AppProps["Component"] & {
PageLayout?: React.ComponentType;
};
emotionCache: EmotionCache;
};
const clientSideEmotionCache = createEmotionCache();
function sApp({
Component,
emotionCache = clientSideEmotionCache,
pageProps,
}: ComponentWithPageLayout): JSX.Element {
const apolloClient = useApollo(pageProps);
const AnyComponent = Component as any;
const Layoutio = Component.PageLayout as any;
const ubt = Ubuntu({
weight: ["300", "400", "500", "700"],
style: ["normal", "italic"],
});
const [showChild, setShowChild] = useState(false);
useEffect(() => {
setShowChild(true);
}, []);
if (!showChild) {
return null;
}
if (typeof window === "undefined") {
return <></>;
} else {
return (
<>
<style jsx global>{`
html {
font-family: ${ubt.style.fontFamily};
}
`}</style>
<CacheProvider value={emotionCache}>
<ApolloProvider client={apolloClient}>
<ThemeColorProvider>
<ChakraProvider theme={theme}>
<ColorModeScript
initialColorMode={theme.config.initialColorMode}
/>
<DefaultSeo {...SEO} />
{Component.PageLayout ? (
<Layoutio>
<AnyComponent {...pageProps} />
</Layoutio>
) : (
<Layout>
<AnyComponent {...pageProps} />
</Layout>
)}
</ChakraProvider>
</ThemeColorProvider>
</ApolloProvider>
</CacheProvider>
</>
);
}
}
export default App;
Simply move the ubt constant above the sApp function :)
Under _app.tsx the minimal code that worked is shown below. Am using chakra. The downside is that this is experimental and worked on some devices and failed on some like Safari.
import { useApollo } from "#api/apollo/apolloclient";
import { EmotionCache, CacheProvider } from "#emotion/react";
import { Ubuntu } from "#next/font/google";
import "focus-visible/dist/focus-visible";
import { DefaultSeo } from "next-seo";
import { AppProps } from "next/app";
import theme from "#definitions/chakra/theme";
import { ThemeColorProvider } from "#definitions/context/theme";
import createEmotionCache from "#definitions/utils/createEmotionCache";
import "#styles/app.css";
import "#styles/global.scss";
import SEO from "../../next-seo.config";
import { ChakraProvider, ColorModeScript } from "#chakra-ui/react";
import { ApolloProvider } from "#apollo/client";
type ComponentWithPageLayout = AppProps & {
Component: AppProps["Component"] & {
PageLayout?: React.ComponentType;
};
emotionCache: EmotionCache;
};
const clientSideEmotionCache = createEmotionCache();
function sApp({
Component,
emotionCache = clientSideEmotionCache,
pageProps,
}: ComponentWithPageLayout): JSX.Element {
const apolloClient = useApollo(pageProps);
const AnyComponent = Component as any;
const Layoutio = Component.PageLayout as any;
const ubt = Ubuntu({
weight: ["300", "400", "500", "700"],
style: ["normal", "italic"],
});
return (
<>
<CacheProvider value={emotionCache}>
<ApolloProvider client={apolloClient}>
<ThemeColorProvider>
<ChakraProvider theme={theme}>
<ColorModeScript
initialColorMode={theme.config.initialColorMode}
/>
<DefaultSeo {...SEO} />
//Wrap component with classname
<main className={ubt.className}>
<AnyComponent {...pageProps} />
</main>
</ChakraProvider>
</ThemeColorProvider>
</ApolloProvider>
</CacheProvider>
</>
);
}
export default App;
For next config add this block in nextconfig.
experimental: {
appDir: true,
fontLoaders: [
{ loader: "#next/font/google", options: { subsets: ["latin"] } },
],},
I ran into this too, But here is my solution .
import React from "react";
import Link from "next/link";
import { Butterfly_Kids } from '#next/font/google';
import Button from "../Button";
const inter = Butterfly_Kids({ weight: "400", subsets:["latin-ext" ] })
You have to specify some properties to your font function object e.g,
Butterfly_Kids({
weight: "400",
subsets:["latin-ext" ],
style : "normal
})
etc
I hope this solve your problem.
I try to make react-i18next package working but i'm facing an issue.
I have a provider for i18n initialization :
import React, {useState, useEffect, useRef} from 'react';
import {useLocalization} from '#shopify/hydrogen';
import {I18nextProvider} from 'react-i18next';
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend';
// Bundle the default translation
import fr from '../assets/locales/fr/translation.json';
i18n
// load other translations dynamically
.use(
resourcesToBackend((language, namespace, callback) => {
import(`../assets/locales/${language}/${namespace}.json`)
.then((resources) => {
callback(null, resources);
})
.catch((error) => {
callback(error, null);
});
})
)
.use(LanguageDetector)
.use(initReactI18next);
export function TranslationProvider({children}) {
const {language} = useLocalization();
const init = useRef(false);
const [i18nInstance, setI18nInstance] = useState(null);
useEffect(() => {
if (init.current) return;
init.current = true;
const cachedLang = localStorage.getItem('i18nextLng');
const defaultLang = language.isoCode.toLowerCase();
if (!cachedLang) {
localStorage.setItem('i18nextLng', defaultLang);
}
// initialize i18next
i18n.init(
{
partialBundledLanguages: true, // This allows some resources to be set on init while others after
fallbackLng: defaultLang,
debug: true,
react: {useSuspense: true, wait: true},
ns: ['translation'],
defaultNS: 'translation',
interpolation: {
escapeValue: false,
},
resources: {
fr: {translation: fr},
},
},
() => {
setI18nInstance(i18n);
}
);
}, [i18n, setI18nInstance, i18nInstance, language]);
return (
i18nInstance && (
<I18nextProvider i18n={i18nInstance}>{children}</I18nextProvider>
)
);
}
Then I call my Provider like this :
<Suspense fallback={'Loading lang..'}>
<TranslationProvider>
<Router>
<FileRoutes
basePath={languageCode ? `/${languageCode}/` : undefined}
/>
<Route path="*" page={<NotFound />} />
</Router>
</TranslationProvider>
</Suspense>
Finally I use it in a component :
import {useTranslation} from 'react-i18next';
const {t} = useTranslation();
{t('footer.language')}
I have no error but it displays :
footer.language instead of the expected translation
When I inspect t, i get [Function: notReadyT]
Does anyone can help me with this ?
Thank you
I use connected-react-router and have some types of pages: main, inner, auth, admin. Depends of current page type I render certain components. Now it works in this way: there are 4 props in my config reducer with bool values: isMainPage, isInnerPage, isAdminPage, isAuthPage, and only one of them could be true in a time. They changes any time I change location (by executing certain action in componentDidMount). So, I want to deal with connected-react-router and, if possible, pass these props from config reducer to the router. Then, if possible, I want to execute an action that will define current page type and set it and then I would get this val in components. Is it all possible? Sorry for this explanation - I'm just studying.
I would provide some code but I don't know which part could be helpful - ask for one, please
config reducer:
import {
SET_VIEW_MODE,
SET_AUTH_PAGE,
SET_MAIN_PAGE,
SET_INNER_PAGE,
SET_ADMIN_PAGE,
...
} from '../constants';
...
case SET_AUTH_PAGE:
return {
...state,
isAuthPage: true,
isMainPage: false,
isInnerPage: false,
isAdminPage: false
};
case SET_MAIN_PAGE:
return {
...state,
isAuthPage: false,
isMainPage: true,
isInnerPage: false,
isAdminPage: false
};
case SET_INNER_PAGE:
return {
...state,
isAuthPage: false,
isMainPage: false,
isInnerPage: true,
isAdminPage: false
};
case SET_ADMIN_PAGE:
return {
...state,
isAuthPage: false,
isMainPage: false,
isInnerPage: false,
isAdminPage: true
};
config actions:
...imports;
export const actionSetViewMode = () => ({ type: SET_VIEW_MODE });
export const actionSetAuthPage = () => ({ type: SET_AUTH_PAGE });
export const actionSetMainPage = () => ({ type: SET_MAIN_PAGE });
export const actionSetInnerPage = () => ({ type: SET_INNER_PAGE });
expample of component that sets any type:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actionSetMainPage } from '../actions/configActions';
...
class MainPage extends Component {
componentDidMount() {
this.props.actionSetMainPage();
}
render() {
return (
<>
...
</>
)
}
}
export default connect(null, {
actionSetMainPage,
})(MainPage);
example of component that renders any depends of page type:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { CSSTransition } from 'react-transition-group';
import Subaction from '../components/header/Subaction';
import Back from '../components/header/Back';
import Menu from '../components/header/Menu';
import Title from '../components/header/Title';
import Logo from '../components/header/Logo';
import Notification from '../components/header/Notification';
class Header extends Component {
render() {
const { isAdaptive, isAuthPage, isMainPage, isInnerPage, title } = this.props.config;
return (
!isAuthPage &&
<header>
<div className="h-inner">
...
{
isMainPage &&
<Logo></Logo>
}
<Notification></Notification>
...
</div>
</header>
)
}
}
export default connect(state => ({
config: state.config
}), {})(Header);
I've just written test file for my component, at the moment it's very rudimentary.. I'm quite inexperience in written test for frontend. I ran yarn test to this test file and it failed miserably..
Here is the message:
Unable to find an element with the text: Please review your billing details...
This is what I have so far for my test:
import React from 'react';
import { render, cleanup, waitForElement } from 'react-testing-library';
// React Router
import { MemoryRouter, Route } from "react-router";
import Show from './Show';
test('it shows the offer', async () => {
const { getByText } = render(
<MemoryRouter initialEntries={['/booking-requests/20-A1-C2/offer']}>
<Route
path="/booking-requests/:booking_request/offer"
render={props => (
<Show {...props} />
)}
/>
</MemoryRouter>
);
//displays the review prompt
await waitForElement(() => getByText('Please review your billing details, contract preview and Additions for your space. Once you’re happy, accept your offer'));
//displays the confirm button
await waitForElement(() => getByText('Confirm'));
});
and this is the component:
// #flow
import * as React from 'react';
import i18n from 'utils/i18n/i18n';
import { Btn } from '#appearhere/bloom';
import css from './Show.css';
import StepContainer from 'components/Layout/DynamicStepContainer/DynamicStepContainer';
const t = i18n.withPrefix('client.apps.offers.show');
const confirmOfferSteps = [
{
title: t('title'),
breadcrumb: t('breadcrumb'),
},
{
title: i18n.t('client.apps.offers.billing_information.title'),
breadcrumb: i18n.t('client.apps.offers.billing_information.breadcrumb'),
},
{
title: i18n.t('client.apps.offers.confirm_pay.title'),
breadcrumb: i18n.t('client.apps.offers.confirm_pay.breadcrumb'),
},
];
class Show extends React.Component<Props> {
steps = confirmOfferSteps;
renderCtaButton = (): React.Element<'Btn'> => {
const cta = t('cta');
return <Btn className={css.button} context='primary'>
{cta}
</Btn>
};
renderLeftContent = ({ isMobile }: { isMobile: boolean }): React.Element<'div'> => (
<div>
<p>{t('blurb')}</p>
{!isMobile && this.renderCtaButton()}
</div>
);
renderRightContent = () => {
return <div>Right content</div>;
};
render() {
const ctaButton = this.renderCtaButton();
return (
<StepContainer
steps={this.steps}
currentStep={1}
ctaButton={ctaButton}
leftContent={this.renderLeftContent}
rightContent={this.renderRightContent}
footer={ctaButton}
/>
);
}
}
export default Show;
what am I missing? Suggestions what else to add to my test file would be greatly appreciated!
Im trying to customize the Drawer for my Expo RN App but after hours and hours of endless trying I decided to just ask for help.
I see im doing just whats docs and other people around the internet are doing:
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
but this in my case, it just does not work. All I see is this big red error: Error Screen
The problem is located in my main navigator:
const MainNavigator = DrawerNavigator(
{
pedidosNavigator: { screen: PedidosNavigator },
menuNavigator: { screen: MenuNavigator },
},
{
contentComponent: props => <PerfilScreen {...props} />,
}
);
If I set it up this way, it works fine but without customization:
const MainNavigator = DrawerNavigator(
{
pedidosNavigator: { screen: PedidosNavigator },
menuNavigator: { screen: MenuNavigator },
})
The problem is when I add this object as the docs suggest:
{
contentComponent: props => <PerfilScreen {...props} />,
}
Or even when I try to add a width property:
{
drawerWidth: 300
}
What the hell Im doing wrong? Here is my full code
import React from 'react';
import { TabNavigator, StackNavigator, DrawerNavigator } from 'react-navigation';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './src/components/reducers';
import MenuScreen from './screens/MenuScreen';
import PerfilScreen from './screens/PerfilScreen';
import PedidosNuevosScreen from './screens/PedidosNuevosScreen';
import CreateEditMenuScreen from './screens/CreateEditMenuScreen';
import AcceptDeclineScreen from './screens/AcceptDeclineScreen';
import PedidosEnCursoScreen from './screens/PedidosEnCursoScreen';
export default class App extends React.Component {
render() {
const MenuNavigator = TabNavigator({
menu: { screen: MenuScreen },
create: { screen: CreateEditMenuScreen }
}
);
const PedidosNavigator = TabNavigator(
{
entrantes: { screen: StackNavigator(
{
entrantes: { screen: PedidosNuevosScreen },
nuevoPedido: { screen: AcceptDeclineScreen }
}
) },
enCurso: { screen: PedidosEnCursoScreen }
}
);
const MainNavigator = DrawerNavigator(
{
pedidosNavigator: { screen: PedidosNavigator },
menuNavigator: { screen: MenuNavigator },
},
{
contentComponent: props => <PerfilScreen {...props} />,
}
);
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
}
Thanks for your help!
After even more hours of looking for an answer, I did find the answer. Drawer Navigator must contain aditional properties to work.
If you are like me, and want to customize the drawer you will also have to include this props to your Drawer Navigator:
{
initialRouteName: 'YourMainRouteAsString',
contentComponent: YourCustomizedComponent,
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle'
}