Display hamburger menu only on breakpoint - javascript

I have created a hamburger menu in react it displays content on breakpoint as expected. But can any body help me to display the hamburger menu icon only on breakpoint otherwise it should display the Navigation menu?

Hey you could use a third-party library like react-responsive and then conditionally render the hamburger menu when a certain device width condition is met. For example, let's say you want to show the <HamburgerMenu /> component when the device with is less than 500px. You could use something like this:
// after installing react-responsive and assuming you have a separate HamburgerMenu and DesktopMenu component
import { useMediaQuery } from 'react-responsive';
import DesktopMenu from './DesktopMenu';
import HamburgerMenu from './Hamburger';
const NavigationMenu = () => {
const isMobile = useMediaQuery({
query: '(max-width: 500px)',
});
return {isMobile ? <HamburgerMenu /> : <DesktopMenu />}
}
---- UPDATE ----
Since you mentioned you don't want to use any third-party libraries, I will add a custom functionality to render components based on window object's innerWidth.
I added a getWindowSize() function that will get the window.innerWidth. Then I will run this function after the page is rendered for the first time using useEffect hook and update the windowsize state. I will also add a resize event to window object to run this function if the window object is being resized. Then based on the windowsize state I will conditionally render what I want (in my example I used 500px as an example. Please see it here:
import { useEffect, useState } from 'react';
import Hamburger from './Hamburger';
import DesktopMenu from './DesktopMenu';
export default function App() {
const [windowSize, setWindowSize] = useState(getWindowSize());
function getWindowSize() {
const { innerWidth } = window;
return { innerWidth };
}
useEffect(() => {
function handleWindowResize() {
setWindowSize(getWindowSize());
}
window.addEventListener('resize', handleWindowResize);
return () => {
window.removeEventListener('resize', handleWindowResize);
};
}, []);
return (
<div>
<h2>Width: {windowSize.innerWidth}</h2>
{windowSize.innerWidth < 500 ? <Hamburger /> : <DesktopMenu />}
</div>
);
}

Related

Delete images if the user is leaving the component React

I have form with a drag and drop component where I can upload images, after this I send these pictures with axios to my backend and save it on server side and then re-render it in a preview mode. My problem is, that if a user uploads some pictures and after that he switches to another page without submitting the form the added pictures are staying on my server side for no reason.
So the question: I want to check inside my component if a user is leaving, show a prompt and if he clicks on the OK button I want to delete all the added pictures from my backend before leaving. How can I detect this?
My simplified snippet:
function MyComp(props) {
const [Images,setImages] = useState([]) // in this array I store the recieved image's URL
const [isBlocking,setIsBlocking] = useState(true) // used for prompt
const handleSubmit = (event) => {
event.preventDefault();
setIsBlocking(false)
}
return(
<Grid container className={classes.mainGrid} direction="row" spacing={2}>
<Grid item xs={4} xl={4}>
<Prompt when={isBlocking} message="There are unsaved datas. Are you sure you want to leave?"/>
<form className={classes.form} onSubmit={handleSubmit}>
... somecode
</Grid>
</Grid>
)
}
export default MyComp
Thanks in advance
Inside React Function Component you can call the prompt when the user is trying to leave , i.e when the component is unmounting
In Class Component of React you can use React componentWillUnmount() and in Function Component You can use useEffect cleanup function
Code for Function Component
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
export default function Home(props) {
useEffect(() => {
return function cleanup() {
alert("unmounting");
//call api and execute your code here
};
}, []);
return (
<div>
<Link to="/home">
On Click I will unmount this component and go to /home
</Link>
</div>
</Link>
);
}
Code for Class Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Test extends Component {
componentWillUnmount() {
alert('unmounting component');
//call your code here
}
render() {
return (
<div>
<Link to='/home'>
On Click I will unmount this component and go to /home
</Link>
</div>
);
}
}
You can check this codesandbox if you want any ref
When you leave the page, the component method componentWillUnmount() fires. I don't recall how this behaves if you were to simply close the browser window as opposed to just navigating away, nor do I recall how you can escape it and stay on the component, but that should at least be a good starting point for you. Obviously you'd have to do a class extending React.Component for this one instead of a straight function.

React Testing Library, Component Unit Tests

I am trying to build a test unit for my simple React Application using React Testing Library. I readed all docs and get stuck in it.
API was created by create React app. One of the feature is that user can change theme. There is setTheme hook that going to change theme "dark" and "light".
App.js
const App = () => {
const [theme, setTheme] = useState('dark');
return ( <div>
<Header theme={theme} setTheme={setTheme} />
</div>)
};
Header.js
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Header = props => {
return (
<header data-testid="header">
<h1><span className="highlight">Github Users</span></h1>
{props.theme === "dark" ?
<FontAwesomeIcon data-testid="button" icon="sun" size="2x" color="#dcba31" onClick={ () => props.setTheme('light') }/>
: <FontAwesomeIcon icon="moon" size="2x" color="#1c132d" onClick={ () => props.setTheme('dark') }/>}
</header>
);
}
export default Header;
In Header component I added arrow function that changes color of theme.
Now I am trying to write a test that's gonna test Header Component.
Expected result is that after first render Header component shall render icon "sun".
After user click on it header shall return icon "moon".
There is something that i try but it's not working as I mention.
Header.test.js
import React from 'react';
import { render, cleanup } from "#testing-library/react"
import '#testing-library/jest-dom/extend-expect';
import { act } from "react-dom/test-utils";
import Header from '../components/Header';
afterEach(cleanup);
describe("Header Component", () => {
it("first render should return a sun icon", () => {
const {getByTestId } = render(<Header />)
expect(getByTestId("header"). // What method uses here? To check if it is icon sun or moon ? )
})
it("after mouse click event should return a moon icon", () => {
const button = document.querySelector("svg"); // it is correct way to add a svg element as a button ?
act( () => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
})
expect(getByTestId("header"). // again what to write here to test it after click ?
})
})
I am sure that there is some other way to check first render and then after click what's Header component rendering. I think that problem is that there is another Component that is rendered conditionaly. If it is text there is no problem, but after render there is svg element with some attributes like icon="sun" / icon="moon".
Live version of project
Github Repo Link
Questions:
How to properly test that Header component ?
How to pass props in test for example I want to use that setTheme hook in test how to do it ?
There's many ways to do this and I can recommend the articles here https://kentcdodds.com/blog/?q=test to get you started. As for your current set-up I'd change some stuff that I find helpful writing unit tests:
Use data-testid to find elements, e.g. "first render should return a sun icon" can be confirmed by expect(getByTestId('svg-sun')).toBeDefined(), which is a pattern I like
Structure your it's like stubbing-rendering-assertions and only test one thing in each test, for instance, in your second it you're lacking a rendering part of the test
Regarding your question regarding passing the props, you can pass it as render(<Header theme={theme} setTheme={setThemeStub}/>) where const setThemeStub = jest.fn(). This allows you to make assertions as expect(setThemeStub).toBeCalledWith(...)

How to close a React Navigation modal with multiple screens in it

I'm using https://reactnavigation.org/ for navigation in a React Native app with a tab navigator as the main stack and a modal with two screens in it (for logging in and configuring the app).
I can't for the life of me figure out how to close the modal from the second screen (SelectItems). From the first screen in the modal I can close it with navigation.goBack().
Both modal screens need a close button. Is there a way to just return back to whatever tab the user was on?
Thanks in advance for any help.
const Tabs = TabNavigator(
{
Search: { screen: Search },
Settings: { screen: Settings }
}
);
// modal with two screens
const Setup = StackNavigator(
{
Login: {
screen: Login
},
SelectItems: {
screen: SelectItems
}
},
{
initialRouteName: 'Login'
}
);
const RootStack = StackNavigator(
{
Main: {
screen: Tabs
},
Setup: {
screen: Setup
}
},
{
mode: 'modal',
headerMode: 'none'
}
);
I found a solution but it isn't perfect.
You can use the popToTop which will go back to the first Scene of your stack and than the goBack will close the modal.
navigation.popToTop();
navigation.goBack(null);
The problem with that is that it will mount again the first scene of the stack, so be sure you dont use setState in you willMount or didMount. Or prevent it.
That's the solution i'm going with for now. I keep looking for a better solution.
Simple and easy solution for react-navigation 5.x (getParent docs):
navigation.getParent()?.goBack();
This works because it grabs the navigator's parent, which is the modal and what you want to dismiss.
NOTE: In older versions of 5.x this was called dangerouslyGetParent. That exists in newer 5.x versions, but is now deprecated. Use that if getParent isn't available in the version of react-navigation that you're using. It isn't actually dangerous: From react-navigation's documentation:
Reason why the function is called dangerouslyGetParent is to warn developers against overusing it to eg. get parent of parent and other hard-to-follow patterns.
This was my solution with v6 in 2022. It closes the modal and navigates away without any weird behaviors (at least in my case).
onPress = () => {
navigation.goBack(); // <-- this fixed it
navigation.navigate("SomeScreen", { id: 123});
}
If you use react-navigation 4.x there is a method navigation.dismiss(). The method dismisses the entire stack and return to the parent stack
https://reactnavigation.org/docs/4.x/navigation-prop/#dismiss
If you are using Stack Navigation you can always move around in the navigation stack using navigation.pop(). For instance, if you want to close two open modals you can call the pop function with parameter value 2:
navigation.pop(2);
Original solution from https://github.com/react-navigation/react-navigation/issues/686#issuecomment-342766039, updated for React Navigation 4:
Create a DismissableStackNavigator:
import React from 'react';
import { createStackNavigator } from 'react-navigation-stack';
export default function DismissableStackNavigator(routes, options) {
const StackNav = createStackNavigator(routes, options);
const DismissableStackNav = ({navigation, screenProps}) => {
const { state, goBack } = navigation;
const props = {
...screenProps,
dismiss: () => goBack(state.key),
};
return (
<StackNav
screenProps={props}
navigation={navigation}
/>
);
}
DismissableStackNav.router = StackNav.router;
return DismissableStackNav;
};
Usage:
Creating your stack:
// modal with two screens
const Setup = StackNavigator(
{
Login: Login,
SelectItems: SelectItems
},
{
initialRouteName: 'Login'
headerMode: 'none'
}
);
Call navigation.dismiss in your screens to close the modal stack.
I was trying to figure this myself and the solution I ended up using was to use navigation.navigate()
Example this.props.navigation.navigate('name of screen you want to go');
Hope this helps!

How to only render modal in the initial route of the stacknavigator?

The initial screen of my stacknavigator has a modal that shows up upon the occurrence of a certain event ... problem is when I navigate to other screens the modal still shows up when the event is triggered. I want it only to show up when the initial route is showing up.
Maybe you can check this.props.navigation.state.routeName so that you know if it's initial screen or not.
I got this from this document.
https://reactnavigation.org/docs/navigators/navigation-prop
How about using state?
import React, { useEffect,useState } from "react";
const MainScreen = (props) => {
const [seenReminder, setReminder] = useState(false);
useEffect(()=>{
if (!seenReminder){
props.navigation.navigate('reminder')
setReminder(true)
}
}, [seenReminder]);
return (
<View ></View>
);
};
export default MainScreen;

React native - Change screen after x seconds

I have a problem that I haven't been able to solve.
In my React native application, I would like to display a welcome screen at the start. Then 5 seconds later just close it, and display another one. Both are 2 entirely different screens, no need to keep the "come back" arrow.
I have been searching for hours, but I haven't found out how to do it.
Here is my code for now:
import Defis from './components/defis'
import Quote from './components/quote'
export default class Betty extends Component {
componentDidMount(){
// Start counting when the page is loaded
this.timeoutHandle = setTimeout(()=>{
// Add your logic for the transition
this.props.navigation.navigate('Defis') // what to push here?
}, 5000);
}
componentWillUnmount(){
clearTimeout(this.timeoutHandle);
}
render() {
return (
<Quote/>
);
}
}
Does anybody know how to do it?
I'm not able to use Navigator.push, moreover Navigator seems deprecated.
Not Using any navigator this can solve your problem
import Defis from './components/defis'
import Quote from './components/quote'
export default class Betty extends Component {
constructor(props){
super(props)
this.state = {
component : <Quote />
}
}
componentDidMount(){
// Start counting when the page is loaded
this.timeoutHandle = setTimeout(()=>{
// Add your logic for the transition
this.setState({ component: <Defis /> })
}, 5000);
}
componentWillUnmount(){
clearTimeout(this.timeoutHandle);
}
render() {
return (
this.state.component
);
I have done this to show login screen after the splash screen in react-native as follows:
import Login from './Login'; // My next screen
....
....
const {navigate} = this.props.navigation;
setTimeout(() => {
navigate('Login'); //this.props.navigation.navigate('Login')
}, 5000); //5000 milliseconds
I have used react-navigation for the navigation purpose.
I was doing almost the same thing with "react-native-router-flux".
Simply render a first screen, in your case the "Quote", and then set in componentDidMount:
setTimeout(() => {
Actions.yourNextSceneName()
}, milliseconds)
Hope this helps.
This worked for me:
import { NavigationActions } from "react-navigation";
componentDidMount(){
setTimeOut( () => {
NavigationActions.navigate('login');
}, 5000 );
}
You can do it with using navigator by returning a View with the onLayout prop and adding the setTimeout function to the prop.
How to navigate to another screen after timeout in React Native:
So I have created my navigation structure and the respective pages already.
Using functional component in ReactNative, do this this to the componentthat you want navigate from:
function MyPresentScreen( {navigation}, props ){
setTimeout(() => {
navigation.navigate('MyTargetScreen');
}, 2500);
return(
<Text>My Present Screen that I will navigate from after some seconds</>
)
};
Note that you can customize the timeout as you wish. The above is 2 and half seconds.
Credit: Even though it was written with class component, this article was helpful in helping me figure this out:

Categories

Resources