React; rendering components from an array depending on what tab im in - reactjs

What I want my code to do
I need to make tabs that display different components depends on the state
My issue
I don't know how to render the correct component using a conditional depending on what tab I'm in with react
What my code is doing so far
I have an array of all my components in the const allTabs
depending on what button i click the state of tabNum changes
import React, {useState} from "react";
import Tabcomp1 from './../components/tabcomp1';
import Tabcomp2 from './../components/tabcomp2';
const allTabs = [Tabcomp1, Tabcomp2];
const Pagetwo = () =>{
const[tabNum, setTabNum] = useState("0");
const changeTab = (e) =>{
e.preventDefault();
if (tabNum === "0") {
setTabNum("1");
console.log(tabNum);
}
else {
setTabNum("0");
console.log(tabNum);
}
}
return(
<div>
<h1>you are now on page 2</h1>
{/* tab1 */}
<button id={0} onClick={changeTab}>tab 1</button>
{/* tab2 */}
<button id={1} onclick={changeTab}>tab 2</button>
<br />
{/* display the correct component here */}
</div>
)
}
export default Pagetwo;

in you button event handler you can set index as
onClick={() => setTabNum(0 // tab index here)}
and below you button you can render it like
{allTabs[tabNum]}

Related

how to call child component callback event from parent component in react

Hi I am a beginner in React, I am using Fluent UI in my project .
I am planning to use Panel control from Fluent UI and make that as common component so that I can reuse it.I use bellow code
import * as React from 'react';
import { DefaultButton } from '#fluentui/react/lib/Button';
import { Panel } from '#fluentui/react/lib/Panel';
import { useBoolean } from '#fluentui/react-hooks';
export const PanelBasicExample: React.FunctionComponent = () => {
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
return (
<div>
<Panel
headerText="Sample panel"
isOpen={isOpen}
onDismiss={dismissPanel}
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
closeButtonAriaLabel="Close"
>
<p>Content goes here.</p>
</Panel>
</div>
);
};
https://developer.microsoft.com/en-us/fluentui#/controls/web/panel#best-practices
I remove <DefaultButton text="Open panel" onClick={openPanel} /> from the example .
So my question is how can I open or close this panel from any other component ?
I would use React useState hook for this.
Make a state in the component that you want render the Panel like
const [openPanel, setOpenPanel] = useState({
isOpen: false,
headerText: ''
})
Lets say for example you will open it from button
<Button onClick={() => setOpenPanel({
isOpen: true,
headerText: 'Panel-1'
})
}> Open me ! </Button>
Then pass the state as props to the Panel component
<PanelBasicExample openPanel={openPanel} setOpenPanel={setOpenPanel} />
in PanelBasicExample component you can extract the props and use it.
export const PanelBasicExample(props) => {
const {openPanel, setOpenPanel} = props
const handleClose = () => {setOpenPanel({isOpen: false})}
return (
<div>
<Panel
headerText={openPanel.headerText}
isOpen={openPanel.isOpen}
onDismiss={() => handleClose}
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
closeButtonAriaLabel="Close"
>
<p>Content goes here.</p>
</Panel>
</div>
);
}

The Side-Bar Menu is rendered by its own

The sidebar is automatically rendered even i have functionality to implement with click on pizza icon
Main.js component , in this component the functionality of useState Hook for toggling the sidebar is implemented
import React, { useState } from 'react';
import { Navbar } from '../NavBar/Navbar';
import { SideBar } from '../SideBar/sidebar';
import { MainContainer,MainContent,MainItem,MainH1,MainP1,MainButton } from './MainElements';
export const Main = () => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
setIsOpen(!isOpen);
};
return (
<MainContainer>
<Navbar toggle={toggle}/>
<SideBar isOpen={isOpen} toggle={toggle} />
<MainContent>
<MainItem>
<MainH1>
Greatest Pizza Ever
</MainH1>
<MainP1>
Ready as early in the 5 Minutes
</MainP1>
<MainButton>Submit</MainButton>
</MainItem>
</MainContent>
</MainContainer>
)
}
Sidebar.js component which is automatically rendered on the screen
import { SidebarContainer,Icon,CloseIcon,SidebarMenu,SidebarLink,SidebarBtn,SidebarRoute } from "./sidebar.element";
export const SideBar = ({isOpen,toggle})=> {
return(
<SidebarContainer isOpen={isOpen} onClick={toggle}>
<Icon onClick={toggle}>
<CloseIcon/>
</Icon>
<SidebarMenu>
<SidebarLink to="/">Pizzas</SidebarLink>
<SidebarLink to="/">Desserts</SidebarLink>
<SidebarLink to="/"> Full Menu</SidebarLink>
</SidebarMenu>
<SidebarBtn>
<SidebarRoute to="/">Order Now</SidebarRoute>
</SidebarBtn>
</SidebarContainer>
);
}
Navbar.js component which holds the icon and props toggle
import React from 'react';
import { Nav, NavLink, NavIcon, PizzaIcon} from './NavbarElements';
export const Navbar = ({toggle}) => {
return (
<Nav>
<NavLink to='/'>
Muncheese
</NavLink>
<NavIcon onClick={toggle}>
<p>Menu</p>
<PizzaIcon/>
</NavIcon>
</Nav>
)
}
Not so sure what your isOpen do. Did you declare that part inside SidebarContainer?
Try changing it to render conditionally like this
{ isOpen ? <SideBar toggle={toggle}/> : null }
Also its worth pointing out that within your sidebar, you've already set an onClick event listener for the whole sidebar component. The onClick within your Icon is not necessary unless you want a specific part of this component to perform the toggle function then you need to remove the onClick in SidebarContainer.
<SidebarContainer isOpen={isOpen} onClick={toggle}>

How do I change the state of the parent component from the child component in Reactjs?

I am learning Reactjs and started building simple project along the line. So, I have two components: the parent and child components which I will call component A and B. There is a button in component A (parent component) that will make component B (child component) to popup and will make component A to be unclickable with transparent background (0.3).
Inside component B, I wrote a code that will remove the component once a botton is clicked. This button is right there in component B. So, the problem now is that once I click this button to remove the popup, component A will remain unclickable with transparent background of 0.3. How can I change the state of component A from component B since component B is the child of component A? Is this possible or I need to rewrite the code entirely?
Thank you as you help me.
Component A codes:
import React, { useEffect, useState } from 'react';
import "./home.css";
import {dataFile} from "./data";
import Addbirth from "../components/addbirthday" //this is component B named Addbirth
export default function Home() {
const [showAdd, setShowAdd] = useState(false);
const handleClick = () =>{
setShowAdd(!showAdd)
}
const removeData = (id) =>{
let showRemainingData = datafile.filter((items) => items.id !== id )
setdatefile(showRemainingData)
}
return (
<>
{showAdd && <Addbirth/> }
<div className="container homeContainer">
<div className={showAdd? "homeWrapper unclickable " :"homeWrapper" }>
<h2 className="headerTitle">You have 5 birthdays today</h2>
<button className="btn" onClick={ handleClick} >ADD BITHDAY</button> {/* This
is the button that calls component B and turns component A with transparent
background and unclickable when clicked*/}
component B:
import React, { useState } from 'react';
import "./addbirthday.css";
import "./home.css";
export default function Addbirthday() {
const [closeInput, setCloseInput] = useState(false);
const closeNow = () =>{
setCloseInput(!closeInput)
}
return (
<div className="container">
<div className= { closeInput? "addContainer" :"addWrapper homeWrapper "}>
<i className="fas fa-window-close" onClick={closeNow} ></i> {/* This button will
close conponent B when clicked. */)
<div className= "addbirth">
<label>Name</label>
<input type="text" placeholder="name"/>
<label>Choose Birthdate</label>
<input type="date" />
<label>Relationship</label>
<input type="text" placeholder="Friend" />
</div>
<button className="addBtn" >Add</button>
</div>
</div>
)
}
Thank you as you help me.
I think i understand what you are asking. You just need to pass down the original component to the new Birthday one and have it change back to clickable. In your Return in component A have:
{showAdd && <Addbirth setShowAdd={setShowAdd} /> }
Then in component B import it as props:
export default function Addbirthday({setShowAdd}) {
const [closeInput, setCloseInput] = useState(false);
const closeNow = () =>{
setCloseInput(!closeInput);
setShowAdd(false);
}

Using a Custom React based Modal, how can I pass a dynamic triggering function so I can re-use the component?

I have the following component which makes up my modal:
import React from 'react';
import { ModalBody, Button, Alert } from 'bootstrap';
import { AppModalHeader } from '../../common/AppModalHeader';
import ModalWrapper from './ModalWrapper';
const QuestionModal= ({
title,
noText = 'No',
yesText = 'Yes',
questionText,
onYesAction
children
}) => {
const { toggle, isOpen, openModal } = useModalForm();
return (
<React.Fragment>
<ModalWrapper className={className} isOpen={isOpen} toggle={toggle}>
<AppModalHeader toggle={toggle}>{modalTitle}</AppModalHeader>
{isOpen ? (
<ModalBody>
<p>{questionText}</p>
<Button
className="float-right"
color="primary"
onClick={() => {
if (onYesAction !== undefined) {
onYesAction(toggle);
}
}}
>
{yesText != null ? yesText : 'Yes'}
</Button>
</ModalBody>
) : null}
</ModalWrapper>
{children({
triggerModal: () => openModal({ id: undefined }),
toggle
})}
</React.Fragment>
);
};
export default QuestionModal;
I want to use it as such, where I can dynamically choose the name of the trigger that opens the modal:
In use e.g. (note: the inner question modal would be repeated, used 4 or 5 times in my application):
....
<QuestionModal
//....params that match up with above
>
{({ triggerModal }) => (
<QuestionModal
//....params that match up with the component
>
{({ triggerModal2 }) => (
<>
<Button onClick={()=>triggerModal();}>Trigger Modal 1</Button>
<div>
<Button onClick={()=>triggerModal2();}>Trigger Modal 2</Button>
</div>
</>
</>
)}
</QuestionModal>
....
How could I achieve this, by extending the question modal to pass a dynamic function? Just because I keep getting stuck in having to think about duplicating the original component, I want to make this component as reusable as I can. Any help would be greatly appreciated.
Thanks in advance
I think you're overcomplicating things. The problem is you're trying to control whether or not the modal is rendered from inside the modal itself. If you really want to have reusable components, it's good to decouple presentation from logic. In your case, you want to have a modal component with all the presentation/layout/styling stuff and pass in via props the actual content.
For example:
import React from 'react';
import { ModalBody, Button, Alert } from 'bootstrap';
import { AppModalHeader } from '../../common/AppModalHeader';
import ModalWrapper from './ModalWrapper';
const QuestionModal= ({
title,
noText = 'No',
yesText = 'Yes',
questionText,
onYesAction
children
}) => {
return (
<React.Fragment>
<ModalWrapper>
<AppModalHeader toggle={toggle}>{title}</AppModalHeader>
<ModalBody>
<p>{questionText}</p>
<Button
className="float-right"
color="primary"
onClick={onYesAction}
>
{yesText}
</Button>
</ModalBody>
</ModalWrapper>
</React.Fragment>
);
};
export default QuestionModal;
Now this is a purely presentational component, it creates a skeleton in which you put the actual content. And for using it, you'll control whether or not the modal is rendered from where it is actually used, like so:
import React, {useState} from 'react';
import QuestionModal from './QuestionModal'
const SomeComponent = (props) => {
const [showModal, setShowModal] = useState(false);
const toggleModal = () => {
setShowModal(!showModal);
}
const yesActionLogic = () => {
// Your yes-action logic...
}
return (
<div>
{showModal ? (
<QuestionModal
title="Sample title",
questionText="Question?"
onYesAction={yesActionLogic}
/>
) : null}
<Button onClick={toggleModal}>Toggle Modal</Button>
{/* The rest of your stuff... */}
</div>
);
}
If you want to create reusable components, it's good practice to not put any business logic on it. Use props to pass in functions that will be triggered from inside the components, and lift all the work to the components that actually hold your business logic.
One of the SOLID principles of software engineering is called Single-responsibility principle, and you can apply it to your React components:
Your Modal component is responsible for displaying data in its correct layout and triggering some set of functions from outside, regardless of what data/logic you pass.
This Modal component will be used by some other component whose responsibility is to show the user a modal with some specific data, at the right time.
So it makes sense that you should toggle your modal from outside.
On a personal note, I like to structure a React app in components that hold only presentational logic, and are used by containers, which are more logic-dense (generally having async requests).

How to toggle class of a div element by clicking on a button that is inside another functional component (another file)?

I want to toggle class of container (file 2) by an onClick on the button that is inside another component file.
The button has already an onClick function and I want to make it so it calls on two functions. Two toggle functions for the button and two class toggles for the container.
Hope it makes sense.
Here is my code so far:
Button component (File 1)
import React, {useState} from "react";
import "./Sort.scss";
const Sort = () => {
const [toggleSortIcon, toggleSortIconSet] = useState(false);
return (
<div className="tools">
<button
onClick={() => toggleSortIconSet(!toggleSortIcon)}
className={`sort ${toggleSortIcon ? "horizontal" : "vertical"}`}>
</button>
</div>
);
}
export default Sort;
Div container component that I want to change the class of (File 2)
import React, {useState} from "react";
import "./WordContainer.scss";
import UseAnimations from "react-useanimations";
const WordContainer = ({title, definition, example}) => {
const [toggleSize, toggleSizeState] = useState(false);
return (
<div className={`container ${toggleSize ? "big" : "small"}`}>
<div className="content">
<h2>{title}</h2>
<p>{definition}</p>
<h3>Example</h3>
<p>{example}</p>
</div>
<img onClick={() => toggleSizeState(!toggleSize)} src="./resize.svg" alt="resize" className="resize"/>
<div className="bookmark">
<UseAnimations
animationKey="bookmark"
size={26}
/>
</div>
</div>
);
}
export default WordContainer;
You could either have a global state management system (redux, or with custom hooks) that you can use to store the icon size.
Or you could simply provide a callback to your component that stores the icon size in a parent component that then feeds it back to your
Something like this:
const [size, setSize] = useState(/* default value */);
render() {
<>
<Sort onSizeToggle={setSize} />
<WordContainer size={size} />
</>
}

Resources