I am trying to navigate to a nested stack, when I use navigation.push(AppRoutes.RescueMeLanding.Name) I get the error:
Couldn't find a 'component', 'getComponent' or 'children' prop for the screen 'RescueMeStackScreen'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.
Any ideas?
const AppRoutes = {
....
RescueMeLanding: {
Name: 'RescueMeStackScreen',
Label: 'Rescue Me',
isProtected: true,
},
....
};
RescueMeStackScreen:
const RescueMeStackScreen = () => {
return (
<RescueMeStack.Navigator
initialRouteName={AppRoutes.RescueMeLanding.Name}
>
<RescueMeStack.Screen
name={AppRoutes.RescueMeLanding.Name}
component={RescueMeLandingScreen}
options={{ headerShown: false }}
/>
<RescueMeStack.Screen
name={AppRoutes.RescueMeMap.Name}
component={RescueMeScreen}
options={{ headerShown: false }}
/>
;
</RescueMeStack.Navigator>
);
RootStackNavigator:
const RootStackNavigator = () => {
return (
<RootStack.Navigator
initialRouteName={AppRoutes.LoginRegistration.Name}
mode="modal"
>
....
<RootStack.Screen
name={AppRoutes.RescueMeLanding.Name}
component={RescueMeStackScreen}
options={{
title: AppRoutes.Register.Label,
headerShown: false,
animationEnabled: false,
}}
/>
....
</RootStack.Navigator>
);
When navigating to nested component, you need to specify parent explicitly.
See this for further information.
Thus, in your case;
navigation.push(AppRoutes.RescueMeLanding, { screen: AppRoutes.RescueMeLanding.Name })
What I also would suggest is name stack differently than the screen. Such as;
navigation.push(AppRoutes.RescueMeLandingStack, { screen: AppRoutes.RescueMeLanding.Name })
Related
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
I have the following component test:
import AutoGeneratedPage from '../../../src/components/AutoGenerate/AutoGenerate.vue'; // <= note the absence of `.vue` extension, here we are importing the JS/TS part of a Double File Component
describe('AutoGenerated Page tests', () => {
it('Auto generated page from JSON should contain all the UI Elements', () => {
cy.mount(AutoGeneratedPage);
cy.get('[data-test="toggle-setting-0"]').eq(false);
cy.get('[data-test="toggle-setting-0"]').focus().click();
cy.get('[data-test="toggle-setting-0"]').eq(true);
cy.get('[data-test="dropdown-setting-3"]').should('have.text', 'Option 1');
cy.get('[data-test="dropdown-setting-3"]').should('have.text', 'Option 2');
cy.get('[data-test="dropdown-setting-3"]').should('have.text', 'Option 3');
});
})
and bump into the following error when running the component test:
What do I miss? https://github.com/khteh/quasar
The first assertion needs changing:
cy.get('[data-test="toggle-setting-0"]').eq(false);
cy.get('[data-test="toggle-setting-0"]').focus().click();
cy.get('[data-test="toggle-setting-0"]').eq(true);
change to
cy.get('[data-test="toggle-setting-0"]').invoke('val').should('eq', false);
cy.get('[data-test="toggle-setting-0"]').focus().click();
cy.get('[data-test="toggle-setting-0"]').invoke('val').should('eq', true);
because .eq(number) is a Cypress command for taking the nth item in a group.
The error Cannot read properties of undefined (reading 'deep') is due to the deep rendering (i.e nested components) in the AutoGenerate.vue component.
If you comment out the child components, the test succeeds.
<div v-for="(field, index) in layoutObj.data" :key="index">
<span>{{field.name}}</span>
<!-- <toggle-setting
v-if="field.type === 'toggle'"
:name="field.name"
:fieldName="field.fieldName"
:description="field.description"
:data-test="`toggle-setting-${index}`"
/>
<pop-up-edit-setting
v-if="field.type === 'popUpEdit'"
:dataType="field.dataType"
:name="field.name"
:fieldName="field.fieldName"
:hint="field.hint"
:data-test="`popup-edit-setting-${index}`"
/>
<drop-down-setting
v-if="field.type === 'dropDown'"
:name="field.name"
:description="field.description"
:fieldName="field.fieldName"
:internalOptions="internalOptions"
:data-test="`dropdown-setting-${index}`"
/> -->
</div>
Of course, the child components are required, but I thought I'd post this in case it gives you clues.
In any case, the Cypress component test is configured correctly, this is the config I used.
const { defineConfig } = require("cypress");
const webpackConfig = require("./webpack.config");
module.exports = defineConfig({
e2e: {
...
},
component: {
devServer: {
framework: "vue",
bundler: "webpack",
webpackConfig,
},
specPattern: 'test/cypress/components/**/*.cy.{js,jsx,ts,tsx}',
indexHtmlFile: 'test/cypress/support/component-index.html',
},
});
Will add to this if I find out the problem with deep-nested components.
Remove mount() and all references to quasar UI stuff. mount() must only be used for Component Tests. Not E2E tests.
I have question and I'm sure it will help other developers.
I have field "is_active" which is Boolean in my API side but it return 0 or 1 and not TRUE or FALSE.
I want to use <FunctionField/> to wrap the <BooleanField/> but it didn't work. Someone can help please.
This is my code:
<FunctionField source="is_active" label="is_active" render={(record) => record.is_active ? true : false}>
<BooleanField/>
</FunctionField>
The column is still blank.
Thanks.
I think you misunderstood the FunctionField component. It renders the result of the render prop. What you are trying to achieve is:
<FunctionField source="is_active" label="is_active" render={(record,source) =>
<BooleanField record={{...record,is_active:!!record.is_active}} source={source}/>}/>
But this is not very nice. Better is to wrap your dataProvider/restClient and ensure the data is a boolean.
// In FixMyDataFeature.js
export default restClient => (type, resource, params) => restClient(type,resource,params).then(response=>
if(resource === 'Resource_with_numeric_is_active_field`){
return {
data: mutateIsActiveFieldToBoolean(response.data)
}
}
else{
return response;
}
);
And call it with Admin:
<Admin dataProvider={FixMyDataFeature(dataProvider)}... />
Here is my solution: (you can import it and use instead of BooleanField)
import React from 'react';
import { BooleanField } from "react-admin";
export const BooleanNumField = ({ record = {}, source}) => {
let theRecord = {...record};
theRecord[source + 'Num'] = !!parseInt(record[source]);
return <BooleanField record={theRecord} source={source + 'Num'} />
}
I had an issue where the in a DB table there was a field called disabled but in the Admin was a bit confusing setting disabled to false to actually enable something.
Based on 'Dennie de Lange' answer, I have created a Typescript generic BooleanOppositeField and BooleanOppositeInput. Putting here hoping may help someone:
import { BooleanField, BooleanInput, FunctionField } from 'react-admin';
interface IProps {
label: string;
source: string;
}
/**
* Usually called using:
* <BooleanOppositeField label="Enabled" source="disabled"/>
*/
export const BooleanOppositeField = (props: IProps) => {
return (
<FunctionField {...props} render={(record: any | undefined, source: string | undefined) =>
<BooleanField source="enabled" record={{ ...record, enabled: !(record![source!]) }} />}
/>
);
};
/**
* Usually called using:
* <BooleanOppositeInput label="Enabled" source="disabled" />
*/
export const BooleanOppositeInput = (props: IProps) => {
return (
<BooleanInput format={(v: boolean) => !v} parse={(v: boolean) => !v} {...props} />
)
}
And you can use it by:
<BooleanOppositeField label="Enabled" source="disabled"/>
or
<BooleanOppositeInput label="Enabled" source="disabled" />
Note: I liked more this solution, than the recommended by Dennie
react native: How to set Image defaultSource on Android.
I have read the React native 1.58 document. I found the Image props defaultSource supported iOS only.
I need to set the default Image when the network Image load error.
I used to write the code like this:
{ ImageUrl?
<Image style={styles.docimg}
source={{uri: ImageUrl}}/>
:
<Image style={styles.docimg}
source={require('../../../resource/default.png')}/>
}
Now there is a problem. When the URL is a string type, but it isn't a correct network Image. As the URL is true then the Image will show nothing.
I saw the Image props onError maybe solve my issue.
I need to set the placeholder Image.
I have tried using #Ravi Raj's answer but seems not related to failure on loading image.
Beside the answer will make the image keep flashing between the default and actual image. ( The error that #vzhen met )
Therefore I have developed based on his answer and generated this component. See if this suits you ;)
progressive-image.js - Component <ProgressiveImage/>
import React, { Component } from 'react';
import { Image } from 'react-native';
export default class ProgressiveImage extends Component {
state = { showDefault: true, error: false };
render() {
var image = this.state.showDefault ? require('loading.png') : ( this.state.error ? require('error.png') : { uri: this.props.uri } );
return (
<Image style={this.props.style}
source={image}
onLoadEnd={() => this.setState({showDefault: false})}
onError={() => this.setState({error: true})}
resizeMode={this.props.resizeMode}/>
);
}
}
Then import and call the component like this:
import ProgressiveImage from './progressive-image';
<ProgressiveImage style={{width: 100, height: 100}}
uri={'http://abc.def/ghi.jpg'}
resizeMode='contain'/>
Hope this answer can help you ;)
You just try this and hope it works...
// initially showDefault will be false
var icon = this.state.showDefault ? require('../../../resource/default.png') : {uri: ImageUrl};
return(
<Image
style={styles.docimg}
source={icon}
onLoadStart={() => this.setState({showDefault: true})}
onLoad={() => this.setState({showDefault: false})}
/>
)
Setting showDefault = false in onLoad() should not trigger url
fetch again since images are cached by default in android and IOS.
Work for me
<ImageBackground
style={{
width: 100,
height: 100,
marginRight: 20,
borderRadius: 10,
}}
source={
require('../../assets/images/Spinner.gif') //Indicator
}>
<Image
style={{
width: 100,
height: 100,
marginRight: 20,
borderRadius: 10,
}}
source={{
uri: `//Image U want To Show`
}}
/>
</ImageBackground>
const [error, setError]=setState(false);
return(){
<Image
onError={(error) => {
setError(true);
}}
source={
error
? require("../../assets/images/defaultImage.png")
: { uri: imageUrl }
}
/>
}
This will show the default image if your network image fails to load or gives an error 404.
According to the docs, the defaultSource prop is ignored on Android on Debug builds, so if you are looking for a placeholder while the actual source is loading -- not in case of errors -- make sure to test on release mode before implementing workarounds to load it only because of debug mode.
"Note: On Android, the default source prop is ignored on debug builds."
defaultSource doesn't work on android...
Please follow the method bellow to fix it:
Dont forget to import View, ImageBackground, & Image
class MyComponent extends Component {
constructor() {
this.state = {
isImageLoading: true,
}
}
render() {
<View>
<ImageBackground
source={{ uri: this.state.isImageLoading ? YOUR_DEFAULT_IMAGE : null }}
>
<Image
source={{ uri: YOUR_IMAGE }}
onLoad={() => this.setState({ isImageLoading: false })}
/>
</ImageBackground>
</View>
}
}
I'm trying to trigger an animation on an hidden element of a child component. To be simple, the animation should occur when the element appears, and then each time a user click on a button from the parent component.
Here is simple code :
(tried to plunkr it, but impossible to import trigger component from angular core)
app.ts
import {ChildComponent} from './child';
#Component({
selector: 'my-app',
template: `
<button id="showChildButton" (click)="setShowChild()">Show Child</button>
<button id="triggerAnimation">Trigger animation</button>
<child-component *ngIf="showChild"></child-component>
`
.....
})
export class App {
showChild: boolean = false;
setShowChild() {
this.showChild = true;
}
}
child.ts
import {
Component,
trigger,
state,
style,
transition,
animate
} from '#angular/core'
#Component({
selector: 'child-component',
template: `<h1 [#inflateIn]>Hello</h1>`,
animations: [
trigger('inflateIn', [
transition('void => *', [
animate(100, style({ transform: 'scale(1.1)'}))
]),
transition('* => *', [
animate(100, style({ transform: 'scale(1.1)'}))
])
])
]
})
export class ChildComponent {
}
I am able to animate the , the first time it appears, but I can't figure out how to trigger this animation again, when clicking on button #triggerAnimation of the parent component.
I searched for examples but I didn't find anything that solve my case.
Thanks for your help
You have to toggle the showChild variable. You can change your setShowChild() method as follows
setShowChild() {
this.showChild === false ? true : false;
}
It checks if this.showChild is false so make it true otherwise false to hide it again.
I hope this is what you wanted to get the desired result?