React Accordion Closing on state change - reactjs

I have 2 tabs which have accordions in those tabs. Whenever user switch between the tabs with accordion open in one tab, again coming back to the previous tab then the accordion is closed. How can we make the accordion to open until user close it.
I am using React Accordion installed from npm.
Here i am in Tab 1 with accordion as opened, as shown in image
Now i switched to tab 3, as shown in image
Now again i switched from tab 3 to tab 2 then you can see here the accordion is closed automatically. Here I don't want to close the accordion on changing of tabs, it should close until user closes it.
This is the problem i am facing here.

You can do this. Have a specific tabs state array for all tabs and store the current state(true=open & false=close) of each tab.
And updating only the state of current tab and keeping the state of rest of the tabs as same.
import { useState } from "react";
import "./styles.css";
export default function App() {
const tabNumbers = 4; //number of tabs
const [tabs, setTabs] = useState([...Array(tabNumbers).fill(false)]); //a state storing state of tabs
const [currentTab, changeTab] = useState(0); //currently active tab
function toggleIndex() {
const updatedTabs = [...tabs];
updatedTabs[currentTab] = !updatedTabs[currentTab]; //only toggle current tab's state
setTabs(updatedTabs); //store updated tab's state
}
return (
<div className="App">
<div style={{ display: "flex", gap: "2rem" }}>
{[...Array(tabNumbers)].map((_, index) => (
<div
style={{ cursor: "pointer" }}
key={index}
onClick={() => changeTab(index)}
>
Tab {index}
</div>
))}
</div>
<div>
<button onClick={toggleIndex}>
This is the tab {currentTab}.<br /> This is currently{" "}
{tabs[currentTab] ? "open" : "close"}
</button>
</div>
</div>
);
}

Related

Link to a `ref` in another component in React

I have a footer component in my React app which contains links to different sections of the website.
Instead of lots of different components to render for each section, I am just making a main component for each section and then within this component there are different parts of the page. i.e
First page
section 1
section 2
section 3
If I want to be able to click a link on the footer to take me to section 3 of the first page, is this possible? Currently I wrote the code in the footer to take me to a new route,
<Grid item xs={12} sm={3}>
<Typography variant='h6'>Golf</Typography>
<List>
<ListItem component={NavLink} color='text.secondary' to='/section-1' sx={{textDecoration:'none', color: 'black'}}>Section 2</ListItem>
<ListItem component={NavLink} color='text.secondary' to='/section-2' sx={{textDecoration:'none', color: 'black'}}>Section 2</ListItem>
</List>
</Grid>
But now I want it to take me to a route '/firstpage' and then to section 1 on that page.
On each page I have all the sections wrapped in a div with a ref and using the useRef hook
const section1 = useRef(null);
const section2 = useRef(null);
const scrollToSection = (elementRef: any) => {
window.scrollTo({
top: elementRef.current.offsetTop - 110,
behavior: 'smooth'
})
}
return (
<>
<div ref={section1}>
//code
</div>
<div ref={section2}>
//code
</div>
</>
)
On each page I have a basic nav system at the top so when I click on a link it scrolls to the relevant section using the scrollToSection function onClick={() => scrollToSection(section2)}
Can I use what I already have to be able to let a user click on a link in the footer and it will take them to the correct route and then automatically scroll to that section.

How to properly display the object value to another separate page component after a button is clicked in React? (From object form to proper form)

I have created a functionality for my "menu item buttons" to display their assigned data through an object format into a separate page component called "SidePage" (gained help also here in StackOverflow). What I am not sure is that if this functionality that I have created is a formal or effective one since I am planning to implement a backend functionality into my full main app. The functionality involves useState, onClick, and onSelected.
I used props to hold the resulting data after the "menu item button" is clicked.
(Chicken Button should be clicked first in order to display the menu item buttons)
SidePage source code:
import * as React from "react";
import { Typography } from "#mui/material";
export default function SidePage(props) {
return (
<div>
<Typography sx={{ mt: 20 }}>Menu Item Details:</Typography>
<div>{props.menuItemDetails}</div>
</div>
);
}
HomeOrderPage (main page) source code:
import * as React from "react";
import { useState } from "react";
import { Stack } from "#mui/material";
import ButtonCategoryStyle from "./ButtonCategoryStyle";
import ChickenButtons from "./categoryButtons/ChickenButtons";
import SidePage from "./SidePage";
const categories = ["Chicken"];
export default function HomeOrderPage() {
const [myCategory, setMyCategory] = useState("");
const [food, setFood] = useState(null);
return (
<div>
<Stack spacing={0} direction="row">
{categories.map((category) => (
<ButtonCategoryStyle
title={category.toLocaleUpperCase()}
key={category}
onClick={() => setMyCategory(category)}
/>
))}
</Stack>
<div>
<p>
{myCategory === "Chicken" && <ChickenButtons onSelected={setFood} />}
</p>
{/* ------------------------------------------------------------------------------------------------ */}
</div>
{/* Displays object after menu item is clicked and renders the side page to show the menu item details:: */}
<div
style={{
backgroundColor: "blue"
}}
>
<SidePage
menuItemDetails={
food && <pre sx={{ ml: 120 }}>{JSON.stringify(food, null, 2)}</pre>
}
/>
</div>
</div>
);
}
Full source code (CodeSandbox): https://codesandbox.io/s/nice-stack-question-page-component-data-transfer-ejebxz?file=/src/HomeOrderPage.jsx
What I also want is to store the property values of the object into variables (in order to display the details of the selected menu item button properly but I am not sure how to do this since I am baffled with using this.state or still using props for this.
Hoping for all of your response as this would help me a lot with my first big React project that I am working on. Thank you very much everyone!
Since you already have props. Why copy it to state? Just keep single source of truth.

Material UI - closing modal leaves focus state on button that opened it

Let's say I have a button that opens a Dialog component. The button has custom theming/styling to specify various states, one of them being the :focus state:
const useStyles = makeStyles({
root: {
"&:focus": {
backgroundColor: "#3A7DA9"
}
}
});
export default function App() {
const [open, setOpen] = useState(false);
const classes = useStyles();
return (
<div className="App">
<Button
id="button-that-opens-modal"
className={classes.root}
onClick={() => setOpen(true)}
>
Open the modal
</Button>
<Dialog open={open}>
<h3>This is the modal</h3>
<Button onClick={() => setOpen(false)}>
Close
</Button>
</Dialog>
</div>
);
}
What I've noticed is that every time I have this pattern, (where a button opens a dialog modal), when the modal is closed, the #button-that-opens-modal is left with a :focus state, which looks bad in terms of styling. Here's a quick gif:
Codesandbox demonstrating the issue
Is this a known issue? I don't see why the :focus should be automatically applied to the button when the modal closes. How can I stop this?
I tried this:
I can add a ref to the button, and make sure to manually unfocus the button in various places. Adding it in the onExited method of the Dialog works, but flashes the focus state for a second:
export default function App() {
const [open, setOpen] = useState(false);
const buttonRef = useRef();
const classes = useStyles();
return (
<div className="App">
<Button
ref={buttonRef}
className={classes.root}
onClick={() => setOpen(true)}
>
Open the modal
</Button>
<Dialog
open={open}
TransitionProps={{
onExited: () => {
buttonRef.current?.blur(); // helps but creates a flash
}
}}
>
<h3>This is the modal</h3>
<Button onClick={() => {setOpen(false)}}>
Close
</Button>
</Dialog>
</div>
);
}
sandbox showing this very imperfect solution
And even if I found exactly the right event handler to blur the button such the styling looks correct, this is not something I want to do for every Dialog in an app that has many Button - Dialog pairs. Is there a Material-UI prop I can use to disable this 'auto-focus' back on the button, rather than having to create a ref and manually .blur it for every Dialog?
This is for accessibilty purpose. You can disable it by adding prop disableRestoreFocus on your Dialog :)

how to render a same component one or multiple time using event handler

I want to build form in Which there are some text fields as shown in picture
in this form there is a button and after clicking the Add Units button a new form will appear
how can i render this sub form by using button onClick Event-Handler and i also want to render it as many times as i click the button , if i click button one time then it shows the the sub form only one time if i click the button two times then it will show two times
-The main issue is I want the sub form to appear as many time i click the button
(optional if i want to remove the rendered sub form using a button click then please mention the code )
use of react hooks is preferable
you can ask me anything related to question
Here is an example of how to solve your problem. You don't need useEffect, and you don't need three separate state variables. You just need one array of objects.
import React, { useState } from "react";
import ContainerSlot from "./ContainerSlot";
function ReceiptContainer() {
const [containers, setContainers] = useState([]);
const handleAddContainer = () => {
// adding an empty object; you will likely need to
// initialize this object with whatever values are stored
// in the container form
setContainers((current) => [...current, {}]);
};
const handleRemove = (index) => {
const current = [...containers];
current.splice(index, 1);
setContainers(current);
};
return (
<div className="receiptContainer container">
{/* Heading */}
<div className="receiptContainer__heading mt-3">
<h4>Container Invoice</h4>
<hr />
</div>
<button onClick={handleAddContainer}>Add container</button>
<div className="receiptContainer__container">
{/* You will likely need to pass whatever values are in
the container to your ContainerSlot component */}
{containers.map((container, index) => {
const handleRemoveClick = () => {
handleRemove(index);
};
return (
<div
key={`container-${index}`}
style={{ display: "flex", alignItems: "center" }}
>
<div>
<ContainerSlot {...container} />
</div>
<div>
<button onClick={handleRemoveClick}>Remove</button>
</div>
</div>
);
})}
</div>
</div>
);
}
export default ReceiptContainer;
EDIT: updated to show how to remove an item

How Can I make my React Button Component updates CSS only on the specific page?

So I created this button component using the following code
const STYLES = ['btn--primary', 'btn--outline', 'btn--test'];
const SIZES = ['btn--medium', 'btn--large'];
const DISPLAY = ['btn--show', 'btn--hidden'];
export const Button = ({
children,
type,
onClick,
buttonStyle,
buttonSize,
buttonDisplay
}) => {
const checkButtonStyle = STYLES.includes(buttonStyle)
? buttonStyle
: STYLES[0];
const checkButtonSize = SIZES.includes(buttonSize) ? buttonSize : SIZES[0];
const checkButtonDisplay = DISPLAY.includes(buttonDisplay)
? buttonDisplay
: DISPLAY[0];
return (
<Link to='/sign-up'>
<button
className={`btn ${checkButtonStyle} ${checkButtonSize} ${checkButtonDisplay}`}
onClick={onClick}
type={type}
>
{children}
</button>
</Link>
);
So I have this button component inside of my navbar component and I also have it inside of my home page section component.
My issue is that whenever I shrink the page to mobile, I want to make the button component in the navbar to display: none and then on the home section I want it to show
What ends up happening is that since it's a component, any CSS style I add to it will go on any other page that is using the component, so basically my button disappears on the home page section when I need it to display
I tried to add an Id to the button component, but that didn't work
<Button id='nav-btn' buttonStyle='btn--outline'>
SIGN UP
</Button>
and I don't know how I'd add a custom class or id to the navbar button without it applying to all the other button components on my homepage
Hide it with an expression, e.g., { showButton && <Button /> }. If showButton is true, you'll see the button, if not, you won't.
If you want to do it via CSS, use a media-query to set display: none on whatever screen size it's supposed to disappear on.
Edit in response to the comment
#media (max-height: 960px) {
display: none;
}
That reads, "If the height is less than 960px, set this property."
If you want a "special" button that hides on a screen size, create a higher-order component that wraps your button.
const NinjaButton => () => {
// do stuff
return <Button cssOverrideProp={cssWithMediaQueryThatHides} />
}
In Button, you can conditionally apply that css,
className=`{/* your other css */ ${cssOverrideProp || ''}}`
Then you can use that button anywhere it's supposed to hide.

Resources