I have a parent componet like this, just to show the dialog
The Child Component ( Main to show dialog)
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false) ///to show dialog
function hanndleDialog() {
setShow(!show) set to show dialog
}
// useEffect(() => {
// if (show == true) {
// setShow(!show)
// }
// },[show])
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show} //pass t
callback={() => {
hanndleDialog()
}}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
And parent componet to show this dialog
export default function DialogTesting(show: boolean, { callback }) {
const [showDialog, doShow] = useState(show) //show to present show in child
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}
>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={showDialog}
title="Record New Progress"
style={DIALOG}
onClose={() => {
doShow(false)
callback()
}}
>
But i cant figure out how to open dialog again when close the dialog, it only open for once, i try React Hook : Send data from child to parent component but not work !
How can i show dialog and when i click close button, the children return orignal state so i can click it again, thank you guy so much
Here is a short video of this problem
https://recordit.co/0yOaiwCJvL
I am assuming that you want to find a way to show hide a component based on click. So this is the sandbox for the same.
In this solution, instead of using a derived state, the state is held in the parent's state and the child is mounted/unmounted based on that state.
The state can be updated by a method present in the parent and this method is passed to the child to be triggered on the "hide child" button. The same method is used to show the child component as well.
Below is the core code for the same,
import React from "react";
const Dialog = ({ hideMe }) => {
return (
<div>
<div>I am dialog</div>
<button onClick={hideMe}>Hide me</button>
</div>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = { showDialog: false };
}
toggleDialog = () => {
this.setState((prevState) => {
return { showDialog: !prevState.showDialog };
});
};
render() {
return (
<div>
<div>I am parent.</div>
<button onClick={this.toggleDialog}>Toggle Dialog</button>
{this.state.showDialog ? <Dialog hideMe={this.toggleDialog} /> : null}
</div>
);
}
}
export default App;
Related
PROBLEM: When I click between each button in my child Sidebar component, the function that I passed down to switch pages between archived and profile doesn't send back the value of state or (the page) to my Dashboard component's RenderComponent switch statement; which, in turn, doesn't render the proper components in my parent Dashboard component.
My parent component Dashboard has the following code:
const [param, setParam] = useState("created");
const RenderComponent = (param) => {
switch (param) {
case "created":
return (
<>
<AddButton />
<PortfolioList />
</>
);
case "archived":
return <Archived />;
default:
return "foo";
}
};
// use function in Sidebar component
const changeParam = (e) => {
setParam(e);
};
And here is the component that renders on the webpage in my Dashboard component:
<>
<Left>
<SideBar changeParam={changeParam} />
</Left>
<Middle>{RenderComponent(param)}</Middle>
</>
Then, I'm passing my changeParam function to my Sidebar component as a prop:
function SideBar({ changeParam }) {
const [state, setState] = useState("created");
const handlePage = (page) => {
setState(page);
// using the function passed from parent component
changeParam(page);
};
return (
<Container>
<Button
onClick={() => handlePage("archived")}
>
</Button>
<Button
onClick={() => handlePage("profile")}
>
</Button>
</Container>
);
}
Any ideas how to make this work?
Change changeParam(state) into changeParam(page): state updates are not immediate.
I have a ReactJS app with 4 screens/components. Each screen can link to another one.
I want to use Modals to display content of each screen, this way I don't lose the state of the current screen.
For now I just set the Modal on my 1st component :
<Modal show={this.state.show}
ref={this.ModalGlobal}
onHide={() => this.setState({show: false})}
>
<Modal.Body>
{this.state.id &&
<MyComponentB id={this.state.id} />
}
</Modal.Body>
</Modal>
On my ComponentB, I want to open the same Modal with different ID.
I tried to use references, but I don't know what to do with that in my ComponentB ?
Like :
this.ModalGlobal.current.destroy
Do I have to use Redux or can it be done using contexts or other solution ?
Instead of having one modal close another one and open that one, would it be possible to instead have the modal update its own contents based on the ID? You could make a wrapper for the modal that will update the body of the modal depending on the current ID. Something like this:
const MyModal = ({id}) => {
const [modalPage, setModalPage] = useState(id);
const [modalIsOpen, setModalIsOpen] = useState(false);
useEffect(() => {
setModalPage(id)
}, [id]);
const openModal = async () => {
setModalIsOpen(true);
document.body.style.overflowY = "hidden";
}
const closeModal = () => {
setModalIsOpen(false);
document.body.style.overflowY = "";
}
const modalPages = {
'welcome': <WelcomeComponent setModalPage />,
'products': <ProductsComponent setModalPage />,
'contact': <ContactComponent setModalPage />
}
const content = modalPages[modalPage];
return (
<Modal
isOpen={modalIsOpen}
onRequestClose={closeModal}
className="react-modal"
overlayClassName="react-modal-overlay"
>
{content}
</Modal>
);
}
This is my parent Component having state ( value and item ). I am trying to pass value state as a props to child component. The code executed in render method is Performing toggle when i click on button. But when i call the list function inside componentDidMount, Toggle is not working but click event is performed.
import React, { Component } from 'react'
import Card from './Components/Card/Card'
export class App extends Component {
state = {
values : new Array(4).fill(false),
item : [],
}
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = this.state.values;
stateObject.splice(index,1,!this.state.values[index]);
this.setState({ values: stateObject });
}
list = () => {
const listItem = this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
this.setState({ item : listItem });
}
componentDidMount(){
// if this is not executed as the JSX is render method is executed everything is working fine. as props are getting update in child component.
this.list();
}
render() {
return (
<div>
{/* {this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
} */}
{this.state.item}
</div>
)
}
}
export default App
This is my child Component where the state is passed as props
import React from 'react'
const Card = (props) => {
return (
<div>
<section>
<h1>Name : John Doe</h1>
<h3>Age : 20 </h3>
</section>
{props.show ?
<section>Skills : good at nothing</section> : null
}
<button onClick={props.toggleHandler} >Toggle</button>
</div>
)
}
export default Card
I know the componentDidMount is executed only once. but how to make it work except writing the JSX directly inside render method
make a copy of the state instead of mutating it directly. By using [...this.state.values] or this.state.values.slice()
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = [...this.state.values]
stateObject = stateObject.filter((_, i) => i !== index);
this.setState({ values: stateObject });
}
Also in your render method, this.state.item is an array so you need to loop it
{this.state.item.map(Element => <Element />}
Also directly in your Render method you can just do
{this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})}
In your card component try using
<button onClick={() => props.toggleHandler()}} >Toggle</button>
Value should be mapped inside render() of the class component in order to work
like this:
render() {
const { values } = this.state;
return (
<div>
{values.map((data, index) => {
return (
<Card
key={index}
show={values[index]}
toggleHandler={() => this.toggleHandler(index)}
/>
);
})}
</div>
);
}
check sandbox for demo
https://codesandbox.io/s/stupefied-spence-67p4f?file=/src/App.js
I have a ButtonGroup with a few Buttons in it, and when one of the buttons gets clicked, I want to change its color, I kinda want to make them behave like radio buttons:
<ButtonGroup>
<Button
variant={"info"}
onClick={(e) => {
..otherFunctions..
handleClick(e);
}}
>
<img src={square} alt={".."} />
</Button>
</ButtonGroup>
function handleClick(e) {
console.log(e.variant);
}
But that doesnt work, e.variant is undefined.
If it was just a single button I would have used useState and I would be able to make this work, but how do I make it work when there are multiple buttons, how do I know which button is clicked and change the variant prop of that button? And then revert the other buttons to variant="info"
Another approach that I could think of is to create my own Button that wraps the bootstrap Button and that way I can have access to the inner state and use onClick inside to control each buttons state, but I'm not sure if that will work, as then how would I restore the other buttons that werent clicked..?
To further from my comment above, you could create your own button component to handle its own state and remove the need to have lots of state variables in your main component e.g.
const ColourButton = ({ children }) => {
const [colour, setColour] = React.useState(true)
return (
<button
onClick={ () => setColour(!colour) }
style = {{color: colour ? "red" : "blue"} }
>
{ children }
</button>
)
}
That way you can just wrap your image in your new ColourButton:
<ColourButton><img src={square} alt={".."} /></ColourButton>
Edit:
I actually like to use styled-components and pass a prop to them rather than change the style prop directly. e.g. https://styled-components.com/docs/basics#adapting-based-on-props
EDIT: Kitson response is a good way to handle your buttons state locally :)
I like to handle the generation of multiple elements with a function. It allows me to customize handleClick.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [buttons, setButtons] = useState([
{
id: 1,
variant: "info"
},
{
id: 2,
variant: "alert"
}
]);
const handleClick = id => {
setButtons(previous_buttons => {
return previous_buttons.map(b => {
if (b.id !== id) return b;
return {
id,
variant: "other color"
};
});
});
};
const generateButtons = () => {
return buttons.map(button => {
return (
<button key={button.id} onClick={() => handleClick(button.id)}>
Hey {button.id} - {button.variant}
</button>
);
});
};
return <div>{generateButtons()}</div>;
}
https://jrjvv.csb.app/
You can maintain a state variable for your selected button.
export default class ButtonGroup extends Component {
constructor(props) {
super(props);
this.state = {
selected: null
};
}
handleClick = e => {
this.setState({
selected: e.target.name
});
};
render() {
const selected = this.state.selected;
return (
<>
<button
name="1"
style={{ backgroundColor: selected == 1 ? "red" : "blue" }}
onClick={this.handleClick}
/>
<button
name="2"
style={{ backgroundColor: selected == 2 ? "red" : "blue" }}
onClick={this.handleClick}
/>
<button
name="1"
style={{ backgroundColor: selected == 3 ? "red" : "blue" }}
onClick={this.handleClick}
/>
</>
);
}
}
Here is a working demo:
https://codesandbox.io/live/OXm3G
I have a parent jsx component that has 2 different jsx components within it.
The one component is a button and the other is a div that opens and closes itself when you click on it (it has a click handler and a state of open or closed).
I now want to add the ability for the button to open and close the div as well.
Is the only way to accomplish this is to pass a handler function down to the button from the parent, moving the div’s open and closed state to the parent component, and pass the state down to the div as props? The reason I ask is that this particular div component is used in a number of different components and removing the open and closed state would affect a lot of different parent components.
Here's a code example of allowing external state manipulation where you can mix the usage of the button or the div to toggle the state. You extend your Collapsible component to use passed props to update the state.
class Collapsible extends React.Component {
constructor(props){
super(props);
this.state = { isOpen: this.props.isOpen !== false };
this.toggleOpen = this.toggleOpen.bind(this);
}
componentWillReceiveProps({ isOpen }) {
this.setState({ isOpen });
}
toggleOpen(){
this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
}
render() {
let display = this.state.isOpen ? null : "none";
return (
<div
className="collapsible"
onClick={this.toggleOpen}
>
<header> header </header>
<div style={{ display }}>{this.props.children}</div>
</div>
);
}
}
class Parent extends React.Component {
constructor(props){
super(props);
this.state = { isOpen: true };
this.toggleOpen = this.toggleOpen.bind(this);
}
toggleOpen(){
this.setState((prevState) => ({ isOpen: !prevState.isOpen }))
}
render() {
return (
<div className="parent">
<Collapsible isOpen={this.state.isOpen}>content</Collapsible>
<button onClick={this.toggleOpen}>
toggle
</button>
</div>
);
}
}
Here is another code example, hope it helps:
I use the local state of the Container and pass this down to the child components. In a bigger app I'd advice you to use something like Redux to manage your state.
The central idea is that the parent component passes a function which can "change it's state" to the button child. It also passed the current isOpen state to the panel. Clicking the button will change the state of the parent, thus triggering a reflow, thus updating the collapsable.
For future reference:
import React from "react";
import { render } from "react-dom";
const Collapsable = ({ isOpen }) =>
isOpen ? (
<div
style={{
border: "1px solid orange",
padding: "1rem",
background: "maroon",
color: "white"
}}
>
{" "}
Hey, I'm open{" "}
</div>
) : (
<div>Oh no...closed :(</div>
);
const Button = ({ openPanel }) => (
<button onClick={() => openPanel()}>Open Panel</button>
);
class Container extends React.PureComponent {
state = { open: false };
openPanel = () => {
this.setState({
...this.state,
open: this.state.open ? false : true
});
};
render() {
return (
<div>
<Button openPanel={this.openPanel} />
<Collapsable isOpen={this.state.open} />
</div>
);
}
}
const App = () => (
<div>
<Container />
</div>
);
render(<App />, document.getElementById("root"));