Element of a screen still found by getByTestID after navigating on another screen - javascript

I hope I am not spamming too much... Actually, I deleted my previous questions to make this one and keep it small and up to the point.
I have a react native application built with expo with a Home screen with one button. Using a stack navigator, this button can enable users to access Screen1. I want to test the following scenario, using jest and #testing-library/react-native :
When the user clicks on the "go to screen1" button, he is sent to Screen1.
So I did this :
it(
'should take user to Screen1 when go-to-screen1 button is pressed',
async () => {
const navigate = jest.fn();
const { getByTestId } = render(<HomeScreen navigation={{ navigate }} />);
fireEvent.press(getByTestId('go-to-screen1-btn'));
waitFor(() => expect(getByTestId('screen1')).toBeTruthy());
},
);
This test succeeds.
But this one succeeds also :
it(
'should take user to Screen1 when go-to-screen1 button is pressed',
async () => {
const navigate = jest.fn();
const { getByTestId } = render(<HomeScreen navigation={{ navigate }} />);
fireEvent.press(getByTestId('go-to-screen1-btn'));
waitFor(() => expect(getByTestId('go-to-screen1-btn')).toBeTruthy());
},
);
Like if the elements on the first page still exists whereas we have moved on another page...
The waitFor is in order to wait for the page change, I tried to remove it but the first test fails if it is removed.
Here is a github repo with the code I describe.
Does someone have an idea ?
Thanks in advance for any help !

Related

NextJs Link causes Error: Loading initial props cancelled

I have nextjs 13 app and I have navbar component with some Nextjs links. After pressing the link and the page starts loading if you click the same link fast some times it throws this error: Error: Loading initial props cancelled.
What I did:
//navbar.ts
const [loadingPath, setLoadingPath] = useState()
useEffect(() => {
Router.events.on('routeChangeComplete', () => setLoadingPath(undefined))
}, [])
const onClickExample= (e) => {
e.preventDefault()
setLoadingPath('/example')
if (loadingPath === '/example' || router.asPath === '/example') return
router.push('/example')
}
//jsx
<Link href="/example" onClick={onClickExample}>Go to Example</Link > // Nextjs Link
There are some discussians like this github issue. It happens on Nextjs 12 too according comments around the net.
This is common user behavior and it's weird to throws such errors.
How do you handle it? Any elegant solution?
I don't see any reason to use onClick in your <Link> here the property href="/example" will make you navigate to /example.
and if you are doing this just to prevent the error then what you are trying to do will not help :
setLoadingPath('/example')
if (loadingPath === '/example')
makes no sens, the loadingPath state will not be updated untill the next render.
This is happening when you try to navigate to another page while the current one is still loading, usually because of a slow connection there are some althernatives like :
const [navigation,setNavigation] = useState('')
//...
const onClickExample = (e) => {
e.preventDefault();
setNavigation("/example"); // when the user clicks you update 'navigation' to the wanted URL to trigger a render
};
//...
useEffect(() => {
if (navigation !== "") { // prevent navigation for the first render
setTimeout(() => {
router.push(navigation); // when the component finishes rendering useEffect runs because navigation is updated then navigate to the wanted URL
}, "500");
}
}, [navigation]);
But you cannot do this with <Link> because it requires href so it will navigate to the given path anyway.
<a onClick={onClickExample}>Go to Example</a>
Try to do it without setTimeout maybe it works for you. In my case it was happening only in development.

Show a message when I submit a form

I'm building a site for a project,I have a divided into 50% 50% with display grid, on the right side there is the form, once sent I would like it to show a confirmation or error message , what can I do? i am using nextjs
the form must disappear and show the message
i could use display none, is there a better method? maybe using the components. Thank you
Well if it were me, ill create a separate feedback component and once the form is submitted, ill send a response be it an error message or success message.
And maybe add a close button plus a timer to close the component.
You can use React portals to access the right side of your app from the left side and render components in it.
I would create a Notification component.
const Notification = ({ message, hideAfterMs }) => {
const [visible, setVisible] = React.useState(true);
React.useEffect(() => {
const timeout = setTimeout(() => { setVisible(false); }, hideAfterMs);
return () => {
clearTimeout(timeout);
}
}, [setVisible]);
if(visible)
return <div>Notification: {message}</div>
return null;
}
Use the portal to render a notification on the side of your app.

How to open the modal of next screen while navigating in react native?

I wanted to open the modal of the another screen (lets say B) from my current screen(lets say A) by passing the props as { showModal: true }.
In my screen A, I have passed the props from A to B like:
Navigation.push(componentId,'B',null,{showModal: true});
In my screen B, I got props showModal and open the modal of screen B like:
useEffect(() => {
// some async API calls
},[]);
useEffect(() => {
if (showModal) {
setTimeout(() => modalRef.current?.setModal(true), 4000); // inside the async function call
}
}, []);
Here you can see i have shown the modal using the ref, but not state because there was other neighbour states which caused re-rendering issue and the modal was not opening. In this case, the modal opens up as i have delayed the opening of the modal since there are some other async API calls as well.
So my question is that is there other alternative solution than this?
I think setTimeout is the wrong approach because anyone does not know about time for APIs fetching so, You can open the model after all APIs successfully fetched.
You can use AppNavigator to go to the next screen.
import {NativeStackNavigationProp} from '#react-navigation/native-stack';
import {AppNavigator} from '#navigators';
const goToFinanceScreen = () => {
navigation.navigate('test');
};

How to make a modal toggle only when navigating from a specific page in React Native?

I have two screens in my app called "ReadyScreen" and "RunningScreen."
The user can access ReadyScreen on one of two ways:
1: When they are getting ready to start a run (when they click the start run button)
2: When they finish the run, they will be navigated from RunningScreen back to the ReadyScreen.
When the user finishes a run, i want a modal to toggle showing them:
How Many Steps They Ran, Average HR, etc.
but I only want it to toggle when they are navigating from "RunningScreen."
Would this require an "if" statement basically stating if they navigated from RunningScreen the modal will toggle, else, it will not? Or is there more nuance to this?
i would send a route param in RunningScreen:
navigation.navigate('ReadyScreen', {
didComeFromRunningScreen: true
})
then in RunningScreen get the variable:
const RunningScreen = ({ route }) => {
const { didComeFromRunningScreen } = route.params
useEffect(() => {
if(didComeFromRunningScreen){
//show modal
}
}, [])
}
i believe this should work.

Run function from a different class in react native

I have a very simple issue with my react native application, I just want to execute a function everytime a button is clicked, it become complicated when there's separate classes and components.
I have 2 screens Dashboard and Search and 2 components Navbar and Results
In Dashboard I grab some user input and store it in selectedIngredients variable
and with the Navbar component I execute a function located in the same file.
<Navbar handle={() => this.switcher()} />
This function is where should the magic happens (or maybe in Search.js screen?)
switcher() {
const { navigate } = this.props.navigation;
navigate('Select', {passedData:this.state.selectedIngredients });
Alert.alert("send data to Search.js")
}
Select is Search.js screen
Everything workin fine and I move to the expected screen with the expected user input selectedIngredients, this is the first render of Search.js screen.
componentDidMount() {
this.apiCall();
Alert.alert("did mount search.js")
}
After that I'm stuck because everytime I click on the btn on my Navbar and execute the switcher() function, componentDidMount do not run anymore so I have to refresh the page by clicking on another button, this is exactly what I'm trying to avoid because it's bad for UX, like really bad. I am not looking to update Results component automatically but just update it with one function.
The code below is not that important it only shows apiCall function and render of Results component, I don't know if I should put more information. Please someone help
apiCall() {
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
data: responseJson.results,
});
})
}
componentDidMount() {
this.apiCall();
Alert.alert("did mount search.js")
}
render() {
return (
<View>
<Navbar title="Results" />
<Results results={this.state.data} />
</View>
);
}
}
My attempt was to add this.props.apiCall() in switcher function but got undefined error, something like hey react native! please send this data to Search.js screen and when you arrive please execute apiCall function, it's located there, not here.
Since you are using react navigation, in Search.js you have to bind didFocus listener, so the api will be called every time your screen is focused
componentDidMount() {
const { navigation } = this.props;
navigation.addListener( 'didFocus', () => this.apiCall() );
this.apiCall();
Alert.alert("did mount search.js")
}

Categories