Wrong pass prop in react component - javascript

I have simple Reactjs app that includes the Card and Modal components. every Card must have a Modal that when clicking on "Show More" button, open it.
Modal should only show the title on its Card and my problem is passed props to Modal, just send the title of the last Card And not about itself!
In summary, the prop of title received properly in Card component but Card component can't pass title to Modal correctly.
Here is my app in CodeSandBox: Demo
Card Components:
const Card = props => {
const { children, title } = props;
const { isShowModal, setIsShowModal } = useContext(MainContext);
const showModalHandler = () => {
setIsShowModal(true);
};
return (
<div className="card">
<div className="card-header">
<h2>{title}</h2>
</div>
<div className="card-content">{children}</div>
<div className="card-footer">
<button onClick={showModalHandler}>Show More</button>
</div>
{isShowModal && <Modal title={title} />}
</div>
);
};
Modal Component:
const Modal = props => {
const { setIsShowModal } = useContext(MainContext);
const closeModalHandler = () => {
setIsShowModal(false);
};
const { title } = props;
return (
<div className="modal">
<h2>Modal: {title}</h2>
<p>
You cliked on <b>{title}</b> Card
</p>
<hr />
<button onClick={closeModalHandler}>Close</button>
</div>
);
};
Note: I use Context for control open/close modal in isShowModal state and maybe that's the problem?

Just as you thought the problem seems to be the useContext that you are using. So I made a couple of changes to the code, most importantly using useState. I recommend you read the documentation about useContext and when to use it. Here is the updated code:
Card.js
import React, { useState } from "react";
import Modal from "./Modal";
import "./Card.scss";
const Card = props => {
const { children, title } = props;
const [ isShowModal, setIsShowModal ] = useState(false);
return (
<div className="card">
<div className="card-header">
<h2>{title}</h2>
</div>
<div className="card-content">{children}</div>
<div className="card-footer">
<button onClick={() => setIsShowModal(true)}>Show More</button>
</div>
{isShowModal && <Modal setIsShowModal={setIsShowModal} title={title} />}
</div>
);
};
export default Card;
Modal.js
import React from "react";
import "./Modal.scss";
const Modal = props => {
const { title } = props;
return (
<div className="modal">
<h2>Modal: {title}</h2>
<p>
You cliked on <b>{title}</b> Card
</p>
<hr />
<button onClick={() => props.setIsShowModal(false)}>Close</button>
</div>
);
};
export default Modal;
As you can see, Modal.js component doesn't have to be a stateful component. You can pass as a prop the setIsShowModal function from Card.js component. That way you can make the modal a reusable component.

Related

Passing Variables to/from Modal

I implemented a Modal which uses a custom Hook in my App that should function as a login mask for a WiFi network. So far the Modal is working and I also already managed to pass the name of the selected SSID to it. However I'm struggling with getting back a variable into my other component from where I launched the modal.
Modal.js:
import React from 'react';
import { createPortal } from 'react-dom';
const Modal = ({ isShowing, connect, ssid, hide }) => isShowing ? createPortal(
<React.Fragment>
<div className="modal-overlay" />
<div className="modal-wrapper" aria-modal aria-hidden tabIndex={-1} role="dialog">
<div className="modal">
<div className="modal-header">
<button type="button" className="modal-close-button" data-dismiss="modal" aria-label="Close" onClick={hide}>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<p>Connect to: {ssid}</p>
<label>
<form onSubmit={connect}>
<input
id="password"
name="Password"
type="password"
value={"password"}
/>
<button type="submit">Connect</button>
</form>
</label>
</div>
</div>
</div>
</React.Fragment>, document.getElementById("modal")
) : null;
export default Modal;
useModal.js:
import { useState } from 'react';
const useModal = () => {
const [isShowing, setIsShowing] = useState(false);
const [password, setPassword] = useState("password");
function toggle() {
setPassword("HOW");
setIsShowing(!isShowing);
}
return {
password,
isShowing,
}
};
export default useModal;
Settings.js:
import React from "react";
import { useState, useEffect } from "react";
import Modal from "../../components/Modal";
import useModal from "../../components/useModal";
const electron = window.require('electron');
const { ipcRenderer } = electron;
const Settings = ({ reqReload }) => {
const [wifiList, setWifiList] = useState([{ id: "", ssid: 'No Networks available' }]);
const [ssidSelected, setSsidSelected] = useState("127.0.0.1");
const { isShowing, toggle } = useModal();
const updateWifi = (event, args) => {
setWifiList(args);
};
function openDialogue(ssid) {
setSsidSelected(ssid);
toggle();
};
/*
function connectWifi(password) {
console.log("Aktuelles Passwort: ", password);
toggle();
}
*/
useEffect(() => {
ipcRenderer.send('updateWifi');
ipcRenderer.on('wifi_list', updateWifi);
return function cleanup() {
ipcRenderer.removeListener('wifi_list', updateWifi);
};
}, []);
return (
<div className="settings">
<Modal
isShowing={isShowing}
connect={connectWifi}
ssid={ssidSelected}
hide={toggle}
/>
<div className="settings__header">
<h2></h2>
</div>
<div className="settings__body">
<div className="settings__connections">
<div className="settings__connections__wifi">
<p><i>Available Wifi-Networks:</i></p>
<div className="settings__connections__wifi__list">
{wifiList.map((item, i) => (
<div className="settings__connections__wifi__list__item" key={i}>
<button className="selectNetworkButton" type="button" onClick={() => openDialogue(item.ssid)}>{item.ssid}</button>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
};
export default Settings;
The code above already contains some things I tried. As stated before, passing the SSID to the modal worked, but I dont have a clue how to get the password back to Settings.js to handle the data there.
I'd be happy if someone can point me in the right direction!
in order to get back a variable from a child component, you can have the variable you would like to get back as a state in your parent component:
const [yourVariable, setYourVariable] = useState('')
then pass setYourVariable as a props to your modal.
this way you can set youVariable from inside the modal component, and get back its value this way :
// inside modal component
setYoutVariable(...)
I stumbled over another article just now and it seems like using a stateless component as my model leads to a dead end. Will have to convert it to a stateful component in order to extract the data from the form.

Optimal way to set state of another component from

there's a few posts explaining how to update state from another component but I'm still unable to parse the solution for my particular problem. I want the submit button in my SideBar.js component to trigger nextProblem() in my tableA.js component when it is clicked. This will cause the state of tableA.js to change as problemNum goes from 1 to 2, generating a different form. I can't import SideBar.js component into my tableA.js due to my design layout though! SideBar.js also contains images. Any ideas?
I'm using a CSS grid for the layout of my page:
grid-template-areas:
"navbar navbar navbar navbar"
"sidebar tableA ... ..."
"sidebar tableA ... ..."
"sidebar tableA ... ..."
I'm using App.js to route to the pages:
function App() {
return (
<Router>
<div className="App">
<Routes>
<Route path={"/"} exact element={<MainPage/>}/>
<Route path={"/gamePage"} exact element={<GamePage/>}/>
</Routes>
</div>
</Router>
);
}
export default App;
I created a SideBar.js component that contains a logo at the top and a submit button at the bottom:
class SideBar extends Component {
componentDidMount() {
}
render() {
return (
<div className={"sidebar"}>
<div className={"logo_group"}>
<img className={"some_logo"} src={logo}/>
</div>
<div className={"nav_group"}>
<button className={"home"}/>
<button className={"about"}/>
</div>
<img className={"an_image"} src={someImage}/>
<div className={"button_group"}>
<button className={"submit_button"} onClick={() => nextProblem()}>Submit</button>
</div>
</div>
)
}
}
export default SideBar
and a tableA.js component with a function nextProblem() that changes the state of the component:
class tableA extends Component {
state = {
problemNum: 1,
problemStatus: 'unsolved',
userAnswer: []
};
nextProblem = () => {
this.setState(state => ({
problemNum: state.problemNum + 1,
}))
}
componentDidMount() {}
render() {
return (
<div>
<form>
...depends on the state of the problem number
</form>
</div>
)
}
export default tableA
I then consolidate everything in GamePage.js:
const GamePage= () => {
return (
<div className={"gamepage-container"}>
<div className={"navbar"}>
<NavBar></NavBar>
</div>
<div className={"sidebar"}>
<SideBar></SideBar>
</div>
<div className={"tableA"}>
<TableA></TableA>
</div>
...
</div>
)
}
export default GamePage
i'd propably go the approach of letting the gamepage handle the state and such and send down the problem and the update function down the components as props. Depending on how big this thing will grow you could also use a centralized store.
const GamePage= () => {
const [problem, setPropblem] = useState(0)
return (
<div className={"gamepage-container"}>
<div className={"navbar"}>
<NavBar></NavBar>
</div>
<div className={"sidebar"}>
<SideBar
onSetNext={() => setProblem(problem + 1)}
></SideBar>
</div>
<div className={"tableA"}>
<TableA
problem={problem}
></TableA>
</div>
...
</div>
)
}
export default GamePage
and in your sidebar the button becomes
<button className={"submit_button"} onClick={() => this.props.onSetNext()}>Submit</button>
similarly you access the props.propblem instead of state in your table.

Create and download image of a react component without rendering it

My use case is simple. I have a react component that takes some props and renders something. Now I want to download it as an image without rendering it basically not showing it to the user.
I have tried html2canvas and react-component-export-image. In both of these libraries, I managed to capture the screenshot but both of them required me to render the component.
I used code from this page for react-component-export-image https://www.npmjs.com/package/react-component-export-image
Suppose the following is my component
const Component1 = ({ reference }) => {
return (
<div ref={reference} >
<div className="share-cause-header">
Some header stuff
</div>
<div className="share-cause-body">
some body stuff
</div>
<div className="share-cause-footer">
some footer stuff
</div>
</div>
);
};
Now on another Component2, I want to send some props to my Component1 and then download that component as an image without showing anything to the user. A user should only see the download button and the downloaded image
const Component2 = () => {
const shareButtonImageDownload = (e) => {
e.stopPropagation();
console.log("Hi there");
};
return (
<div >
<button onClick={shareButtonImageDownload } ></button>
</div>
);
};
If the component is not visible, then you can't take a screenshot of it. But you can bring them out of the visible view, like this:
https://codesandbox.io/embed/laughing-http-w3ndr?fontsize=14&hidenavigation=1&theme=dark
import {
exportComponentAsJPEG,
exportComponentAsPDF,
exportComponentAsPNG
} from "react-component-export-image";
import React, { useRef } from "react";
const ComponentToPrint = React.forwardRef((props, ref) => (
<div ref={ref} style={{ marginTop: "-50px" }}>
<div className="share-cause-header">Some header stuff</div>
<div className="share-cause-body">some body stuff</div>
<div className="share-cause-footer">some footer stuff</div>
</div>
));
const MyComponent = () => {
const componentRef = useRef();
return (
<React.Fragment>
<ComponentToPrint ref={componentRef} />
<button onClick={() => exportComponentAsJPEG(componentRef)}>
Export As JPEG
</button>
<button onClick={() => exportComponentAsPDF(componentRef)}>
Export As PDF
</button>
<button onClick={() => exportComponentAsPNG(componentRef)}>
Export As PNG
</button>
</React.Fragment>
);
};
export default MyComponent;

How to change component that is rendered?

I'm super new to React.js. I'm making changeable layouts using React.js. so i tried to use useState for rendering specific layout that I should click. so I tried to add setState for changing false in a function and made one another setState in the same function. but Too many re-renders Error came out. so what can i use for making changeable layout??
this is my code
import React, { useState } from "react";
import Panel from "./Panel";
import PanelTwo from "./PanelTwo";
import styled from "styled-components";
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => setShowPanel(true);
const handleOnClick1 = () => setShowPanel1(true);
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick1}>
<h1>Panel (2+2) </h1>
</div>
<div>
<h1>Panel (2+3)</h1>
</div>
<div>
<h1>Panel (2+4)</h1>
</div>
<div>
<h1>Panel (3+1)</h1>
</div>
<div>
<h1>Panel (3+2)</h1>
</div>
</Main>
{showPanel ? <Panel /> : null}
{showPanel1 ? <PanelTwo /> : null}
</div>
);
}
and if i do this, it looks like this when i click two buttons
enter image description here
so i tried this .
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => setShowPanel(true);
const handleOnClick1 = () => setShowPanel(false);
setShowPanel1(true);
and i got Too many re-renders Error.
this is what i want to do
enter image description here
when i click each buttons then the exact only one layout is gonna show up .
but the problem is that it's overlapped when i click two buttons
like this
enter image description here
I'm sorry if my explanation is not good.
It seems like you really just want to toggle between the two layouts. You can do this with a single state value, callback, and conditional render via ternary.
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const handleOnClick = () => setShowPanel(show => !show);
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick}>
<h1>Panel (2+2) </h1>
</div>
...
</Main>
{showPanel ? <Panel /> : <PanelTwo />}
</div>
);
}
OFC, this assumes you want to always show at least one of the layouts. If you want to start with both initially hidden (i.e. false) then you can toggle the other panel state false in the handlers to do that.
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => {
setShowPanel(true);
setShowPanel1(false);
};
const handleOnClick1 = () => {
setShowPanel(false);
setShowPanel1(true);
};
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick1}>
<h1>Panel (2+2) </h1>
</div>
...
</Main>
{showPanel && <Panel />}
{showPanel1 && <PanelTwo />}
</div>
);
}

Extracting HTML from a stateless React component that's been passed into a trigger event

I'm trying to create a reusable modal that is rendered high in the DOM (direct child of <body>), and gets content passed to it from wherever.
I have to set the state of the modal with something like a trigger event (unless I'm overlooking another option). Redux is not an option, as I don't have it in the app.
My problem is that when I pass the component containing the content into the trigger event, it renders just the object, but none of the html. It makes sense to me why it works like this, but I can't seem to find a way to extract the content from that object.
My modal:
import React from "react"
import PropTypes from "prop-types"
import Rodal from 'rodal'
class ApplicationModal extends React.Component {
state = {
modalIsOpen: false,
htmlContent: ""
}
componentDidMount() {
$(window).on('modalToggle', (e, content) => {
this.modalToggle(() => this.setModalContent(content))
})
}
setModalContent = (content) => {
this.setState({htmlContent: content})
}
modalToggle = (callback) => {
this.setState({modalIsOpen: !this.state.modalIsOpen}, callback())
}
modalClose = () => {
this.setState({modalIsOpen: false})
}
modalOpen = () => {
this.setState({modalIsOpen: true})
}
render () {
return (
<React.Fragment>
<Rodal visible={this.state.modalIsOpen} onClose={this.modalClose} closeOnEsc={true} className={this.props.rodalClasses}>
<div id="modal-container">
<div dangerouslySetInnerHTML={{__html: this.state.htmlContent}}></div>
</div>
</Rodal>
</React.Fragment>
);
}
}
export default ApplicationModal
My page:
import React from "react"
import PropTypes from "prop-types"
class MyPage extends React.Component {
render () {
// This works
const html = `
<div className="modal-content">
<p>This is a question.</p>
<p>This is an answer.</p>
</div>
`
// This does not work
const ModalContent = () => (
<div className="modal-content">
<p>This is a question.</p>
<p>This is an answer.</p>
</div>
)
return (
<React.Fragment>
<h1>My page</h1>
{/* This works */}
<a href='javascript:void(0)' onClick={() => $(window).trigger('modalToggle', html)}>Learn more</a>
{/* This does not work */}
<a href='javascript:void(0)' onClick={() => $(window).trigger('modalToggle', <ModalContent/>)}>Learn more</a>
</React.Fragment>
);
}
}
export default MyPage
I'd like to be able to pass full components into the trigger event, so the content can be rendered with buttons and dynamic inputs.
Am I going about this completely wrong?
In case it's not obvious, Rodal is just a pretty modal library.
What you're describing can be accomplished with Reactstrap (A bootstrap library specific for React) and the built in component Modal:
import React from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
const ApplicationModal = props => {
const modalBody = (
<div className="modal-content">
<p>This is a question.</p>
<p>This is an answer.</p>
</div>
);
return (
<div>
<Button color="danger" onClick={props.toggle}>
{props.props.buttonLabel}
</Button>
<Modal
isOpen={props.modal}
toggle={props.toggle}
className={props.className}
>
<ModalHeader toggle={props.toggle}>Modal title</ModalHeader>
<ModalBody>{modalBody}</ModalBody>
<ModalFooter>
<Button color="primary" onClick={props.toggle}>
Do Something
</Button>{" "}
<Button color="secondary" onClick={props.toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
);
};
export default ApplicationModal;
Turns out the dangerouslySetInnerHTML was messing it up. I guess it was trying to render straight HTML, rather than rendering the component I passed in as an executable function. Which... Makes sense now that I think about it...

Categories

Resources