How to toggle component display in reactjs - javascript

I am currently learning reactjs so I decided to build a react to-do list, am trying to toggle the display of a component but keep running into errors. Please what is the best way to toggle the display of the Action component when the user clicks on the icon with the class 'ri-menu-line'? This is my snippet.My Code snippet
I tried using react useRef to toggle between an empty string and a className and pass it as a prop to the Action component and then, in the Action component add the prop to the section classList but i keep running into huge errors.

Try doing this:
function App() {
const [show, setShow] = useState(false);
return (
<body>
<header>
<h1></h1>
<i onClick={() => setShow(!show)}></i>
<main>
{show && <Action />}
<Detail />
</main>
</header>
</body>
);
}

Toggle state to true/false.
Use && to display or hide component. see https://beta.reactjs.org/learn/conditional-rendering
import { useState } from "react";
export default function App() {
const [describeAppear, setDescribeAppear] = useState(false);
const onClick=()=>{
setDescribeAppear(!describeAppear)
}
return (
<div className="App">
<button onClick={onClick}>Toggle</button>
{describeAppear && <h2>Start editing to see some magic happen!</h2>}
</div>
);
}

Related

Button not executing both functions when passing state and props

I'm new to react and have encountered an issue which I haven't found any solution to for a while now.
The NEXT button is a child component of Form1 and is declared in App.js. The parameters of Form1 file is (props, {transform, fram}). The intention with props is to declare {props.children} in Form1 so that it allows the Next button to be shown by being implemented in App.js.
When implemented in this manner, the next button only seem to execute 1 function rather than 2 - handleNext() but not fram(). Fram() sets translateX value to form1. handleNext controls the state of CustomizedSteppers.
However, if the "props" is deleted from Form1, and the button is moved out of the tags and put for example above CustomizedSteppers tag, it executes both functions. I want the button to be implemented in the manner that is shown below but it does not work as intended
My App.js:
import {Form1, Form2, Form3, Form4} from './components';
import {Header} from './containers';
import {Section} from './containers';
import {Forms} from './containers'
import CustomizedSteppers from './components/stepper/demo';
const App = () => {
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
const [transform, transformit] = React.useState("TranslateX(0px)");
const fram = () => {
transformit("TranslateX(-900px)");
};
return (
<>
<Header />
<Section class="section">
<CustomizedSteppers activeStep={activeStep} handleNext={handleNext}/>
<Forms>
<Form1 transform={transform} fram={fram}>
<button id="B "onClick={() => {handleNext();fram();}}>NEXT</button>
</Form1>
<Form2 />
<Form3 />
<Form4 />
</Forms>
</Section>
</>
);
}
export default App;
My Form1.js:
export default function Form1(props, {transform, fram}){
return (
<div id='Form1' style={{transform: transform}}>
<p id="demo"></p>
<div class="btn-box-f1">
{props.children}
</div>
</div>
)
}
Instead of trying to call two functions, call the function that updates the state first, and then use useEffect to monitor that state change.
The useEffect:
useEffect(() => {
transformit('TranslateX(-900px)');
}, [activeStep]);
And the updated button:
<button id="B" onClick={handleNext}>NEXT</button>
Oh, and there's no need to have double parentheses in your setActiveStep function:
setActiveStep(activeStep + 1);
As far as I can tell, the issue is with the way you've declared the Form1 component's props. React components take only a single argument, the props object. function Form1(props, { transform, fram }) is invalid, the transform prop isn't accessible in the component, it's undefined.
Here's the working version:
function Form1({ children, transform }) {
return (
<div id="Form1" style={{ transform: transform }}>
<p id="demo"></p>
<div className="btn-box-f1">{children}</div>
</div>
);
}
I've dropped logs in both callbacks and correctly see both triggered and see the transform applied to the id="Form1" div element.
Here the "next" button was clicked, both callbacks logged, the active step state updated to 1, and the transform was applied.

JavaScript React: Issues with passing prop from a function in a child component to a function in the parent

I'm dealing with a problem passing a prop to a parent component from it's child.
The idea of the code that I'm trying to make work is a set of buttons in a header component that when clicked, load new component pages for other parts of the website. I'm dealing with a couple of smaller bugs that I can fix at another time but the primary issue is when I try to pass the results of the function for handling the switch and the values showing as 'undefined' once they get to the App component. I doubt I'm explaining it well so allow me to show the code.
Parent Component (App)
import React from "react";
import Header from "./Components/Header/Header";
import Footer from "./Components/Footer/Footer";
import Pane from "./Components/Pane/Pane";
import MainPane from "./Components/Pane/MainPane";
import BookViewPane from "./Components/Pane/BookViewPane";
import AddBookPane from "./Components/Pane/AddBookPane";
import SignInPane from "./Components/Pane/SignInPane";
import "./App.css";
const App = ()=>{
function LoadPaneHandler(props){
var NewPaneName=props.paneName;
// const NewPaneName={
// name: props.paneName
// };
// const NewPaneName=String(props);
console.log(NewPaneName);
switch(NewPaneName){
case 'MainPane':
return <MainPane />
case 'AddBookPane':
return <AddBookPane />
case 'BookViewPane':
return <BookViewPane />
case 'SignInPane':
return <SignInPane />
default:
return <Pane />
}
}
return(
<React.Fragment>
<Header switchPane={LoadPaneHandler} />
<main>
<LoadPaneHandler paneName="MainPane" />
</main>
<Footer />
</React.Fragment>
);
}
export default App;
Child Component (Header)
import React from "react";
import "./Header.css";
const Header=(props)=>{
var paneName="";
const switchPaneHandler=event=>{
event.preventDefault();
console.log(paneName);
props.switchPane(paneName);
}
return(
<header id="header">
<div id="header-title">
<h1>Library</h1>
</div>
<div id="header-buttons">
<button onClick={paneName="BookViewPane",switchPaneHandler}> View Books</button>
<button onClick={paneName="AddBookPane",switchPaneHandler}> Add Books </button>
<button onClick={paneName="SignInPane",switchPaneHandler}> Login</button>
</div>
</header>
);
}
export default Header;
I've included the commented out code of other approaches I've used to get the data I need for the function to work properly so that you can have an Idea of what I've already tried.
The code works fine so long as I only pass values to the function from within the App component. Whenever I click on one of the buttons in the header though, it shows the 'paneName' correctly in the 'switchPaneHandler' function but then in 'LoadPaneHandler' it prints as 'undefined'.
I'm still quite new to React so it's likely a very obvious mistake that I've made but any help is appreciated all the same. Thanks!
I think the key issue here is probably caused by confusion about "What are props? What is state?" - very common when getting started with React.
If we look at the parent component first, you're passing LoadPaneHandler to your Header like it's a callback function. That's not how we do it in React. We need to supply a callback function that takes the name of the pane that we want the parent to show. Naming can really help too in order to make things clearer. Here's how I'd rewrite your parent:
const App = ()=>{
const [currentPaneName, setCurrentPaneName] = React.useState("MainPane")
function updateCurrentPane(newPaneName) {
console.log(`Updating pane from ${currentPaneName} to ${newPaneName}`);
setCurrentPaneName(newPaneName)
}
function LoadPaneHandler(){
console.log(`Showing pane ${currentPaneName}`);
switch(currentPaneName){
case 'MainPane':
return <MainPane />
case 'AddBookPane':
return <AddBookPane />
case 'BookViewPane':
return <BookViewPane />
case 'SignInPane':
return <SignInPane />
default:
return <Pane />
}
}
return(
<React.Fragment>
<Header onNewPaneSelected={updateCurrentPane} />
<main>
<LoadPaneHandler />
</main>
<Footer />
</React.Fragment>
);
}
I've left a sprinkling of console.logs in there so you can get a feel for the timing of re-rendering and responding (React-ing) to change. If you're not familiar with useState(), this is the one React hook you absolutely must understand if you're going to build useful React applications - here are the docs.
Now for the child. You've got a paneName variable but you don't need it. All you're really trying to do is set up the right argument when you call the callback:
const Header=(props)=>{
const switchPaneHandler= (paneName) =>{
event.preventDefault();
console.log(paneName);
props. onNewPaneSelected(paneName);
}
return(
<header id="header">
<div id="header-title">
<h1>Library</h1>
</div>
<div id="header-buttons">
<button onClick={() => switchPaneHandler("BookViewPane")}> View Books</button>
<button onClick={() => switchPaneHandler("AddBookPane")}> Add Books </button>
<button onClick={() => switchPaneHandler("SignInPane")}> Login</button>
</div>
</header>
);
}
Note I changed the name of the callback function it's expecting to be given as a prop - switchPane was a bit misleading - it's not this component's job to switch panes, it just needs to be able to tell someone that the user wants to switch. This also makes it easier to make changes in the future, for example if you have other things that are interested in which pane the user wants to see.
Try to change your child component (Header) like this.
import React from "react";
import "./Header.css";
const Header = (props) => {
const switchPaneHandler = paneName => {
console.log(paneName);
props.switchPane(paneName);
}
return(
<header id="header">
<div id="header-title">
<h1>Library</h1>
</div>
<div id="header-buttons">
<button onClick={()=> switchPaneHandler('BookViewPane')}> View Books</button>
<button onClick={() => switchPaneHandler("AddBookPane")}> Add Books </button>
<button onClick={() => switchPaneHandler("SignInPane")}> Login</button>
</div>
</header>
);
}
In header component your passing the paneName directly, but in parent component you are trying to get as javascript object (props.paneName), that is why you are getting error.
const switchPaneHandler=event=>{
event.preventDefault();
console.log(paneName);
props.switchPane(paneName);
}
so try to access directly,
function LoadPaneHandler(paneName){
var NewPaneName = paneName;
console.log(NewPaneName);
switch(NewPaneName){
case 'MainPane':
return <MainPane />
case 'AddBookPane':
return <AddBookPane />
case 'BookViewPane':
return <BookViewPane />
case 'SignInPane':
return <SignInPane />
default:
return <Pane />
}
}

using ref.current as container in react portal throws Target container is not a DOM element

I am using react portal to render inside dom element rendered by parent element
import { useRef, useState } from "react";
import { createPortal } from "react-dom";
export default function App() {
const pageTitleRef = useRef(null);
const [page, setPage] = useState(true);
return (
<div className="App">
<header>
<h1>
App-<span id="page-title" ref={pageTitleRef}></span>
</h1>
<button
onClick={(e) => {
setPage((p) => !p);
}}
>
toggle page
</button>
</header>
{page ? (
<Page1 key="page1" pageTitleRef={pageTitleRef} />
) : (
<Page2 key="page2" pageTitleRef={pageTitleRef} />
)}
</div>
);
}
function Page1({ pageTitleRef }) {
return (
<div className="Page1">
{createPortal("Page2 title", pageTitleRef.current)}
<h2>Page 1 content!</h2>
</div>
);
}
function Page2({ pageTitleRef }) {
return (
<div className="Page2">
{createPortal("Page1 title", pageTitleRef.current)}
<h2>Page 2 content!</h2>
</div>
);
}
however it throws an error Target container is not a DOM element.
https://codesandbox.io/s/xenodochial-mendeleev-b5pbr?file=/src/App.js
The problem is that you're trying to use pageTitleRef.current before it has anything in it. It doesn't have anything in it until App renders, but during the rendering of App, you're trying to call createPortal (from Page1 and Page2).
You'll need to do some refactoring; some options:
Render normally in that area, rather than having Page1 and Page2 reach out to it via createPortal; App already knows what page it's on.
Put the portal area in your skeleton HTML, rather than rendering it with App.
Hold the content that should be in the header in App state and pass a setter function to Page1 and Page2 they can use to set that content.
I'd go with #1, Page1 and Page2 seem to have too many responsibilities (page header and page body). You could split them up into PageHead1/PageHead2 and PageBody1/PageBody2. But if it makes more sense, either #2 or #3 could work too.

How to pass a React Hook to a child component

I want to pass the setter of a React Hook to a Child Component. So that a button in the child component updates the state via setter which is saved in the Parent Component. I tried following setup but I get an error message :
TypeError: setshowOptionPC is not a function
onClick
Is my approach even possible? And if not how could I possibly do that structure using a React Hook.
Below a simplified version of my code:
import React, { useState } from "react";
function ChildComponent({ setshowChildOptionBC, setshowChildOptionPC }) (
<div>
<button
onClick={() => {
setshowChildOptionPC(false);
setshowChildOptionBC(true);
}}
>
BC
</button>
<button
onClick={() => {
setshowChildOptionPC(true);
setshowChildOptionBC(false);
}}
>
PC
</button>
</div>
);
function ParentComponent() {
const [showOptionBC, setshowOptionBC] = useState(true);
const [showOptionPC, setshowOptionPC] = useState(false);
return (
<div>
<ChildComponent
setshowChildOptionBC={setshowOptionBC}
setshowChildOptionPC={setshowOptionPC}
/>
{showOptionBC && <div>BC</div>}
{showOptionPC && <div>PC</div>}
</div>
);
}
export default ParentComponent;
I think you just forgot to destructure props in your child component.
This might help.
function ChildComponent({setshowOptionBC, setshowOptionPC}) {..

Odd behavior with react-modal

I'm trying to build a quiz that uses react-modal to provides hints. I will need multiple modals inside the quiz. I'm new to React so it's quite possible that I'm making a simple mistake.
I'm not sure it matters, but I've built this using create-react-app.
My App.js looks like this:
import React, { Component } from 'react';
import HintModal from './hintModal';
import Modal from 'react-modal';
import './App.css';
Modal.setAppElement('#root');
class App extends Component {
state = {
modalIsOpen: false,
hint: ''
};
openModal = (hint) => {
this.setState({ modalIsOpen: true, hint: hint });
}
closeModal = () => {
this.setState({ modalIsOpen: false, hint: '' });
}
render() {
return (
<React.Fragment>
<h1>Modal Test</h1>
<h2>First Modal</h2>
<HintModal
modalIsOpen={this.state.modalIsOpen}
openModal={this.openModal}
closeModal={this.closeModal}
hint="mango"
/>
<hr />
<h2>Second Modal</h2>
<HintModal
modalIsOpen={this.state.modalIsOpen}
openModal={this.openModal}
closeModal={this.closeModal}
hint="banana"
/>
</React.Fragment>
);
}
}
export default App;
hintModal.jsx looks like this:
import React, { Component } from 'react';
import Modal from 'react-modal';
const HintModal = (props) => {
const {openModal, modalIsOpen, closeModal, hint} = props;
return (
<React.Fragment>
<button onClick={ () => openModal(hint) }>Open Modal</button>
<Modal
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel="Example Modal"
>
<h2>Hint</h2>
<p>{hint}</p>
<button onClick={closeModal}>Close</button>
</Modal>
<p>We should see: {hint}</p>
</React.Fragment>
);
};
export default HintModal;
Here's the problem: I need the content of the modal to change based on the hint prop passed to HintModal. When I output hint from outside <Modal>, it behaves as expected, displaying the value of the prop. But when I output hint within <Modal>, it returns "banana" (the value of the hint prop for the second instance of HintModal) when either modal is activated.
Any help would be greatly appreciated!
You are controlling all of your modals with the same piece of state and the same functions to open and close the modal.
You need to either have just one modal and then dynamically render the message inside it or you need to store a modalIsOpen variable in your state for every single modal.

Categories