I have a simple problem in React JS. I have two different click events, which switch the state of the component. The first one works perfectly, however I cannot get the second event to reset the component back to its original state. This is a stripped down version of my problem, so just know that I cannot move the click functions into the Child component.
class Parent extends Component{
constructor(){
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
</div>
)
}
}
Like I said, the handleOpen function switches the state, but the handleClose does not switch it back. I can get a console log to show on the handleClose function, so I know that it does not have to do with how it is being hooked up to the Child Component. Am I missing something about how to reset a state value after it has already been switched. Thank you for your help!
Here is How you have to do it!
class Child extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props.isOpen);
if (this.props.isOpen) {
this.props.onClose();
} else {
this.props.onOpen();
}
}
render() {
return <button onClick={this.handleClick}>Click ME</button>;
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<p>{this.state.open.toString()}</p>
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
</div>
)
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('container')
);
Related
I am trying to open and close a dialog on a button click from another page/component.
But it is not working on clicking the button.
Any sugegstion what I am missing and doing wrong here with handeling modal.
Thanks in advance.
//TestComponent
class TestConnectDialog extends React.Component {
render() {
const {isOpen, onOk} = this.props;
return (
<Dialog
isopen={this.props.isopen}
onClose={this.props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleClose} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
// Home page
import TestConnectDialog from './TestConnectDialog';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
this.handleTestConnectClick = this.handleTestConnectClick.bind(this);
//this.handleCloseDialog = this.handleCloseDialog.bind(this);
}
handleTestConnectClick= () =>{
this.setState({ isOpen: true });
}
render() {
const {isOpen, onOk} = this.props;
return (
<div className="section">
<Button className="connect-test-button"
onClick={this.handleTestConnectClick}>
Test
</Button>
<TestConnectDialog isOpen={this.state.isOpen} />
</div>
);
}
};
export default HomePage;
Your prop name is spelled incorrectly, it should be this.props.isOpen also a quick little tip, it is possible to use just one function for opening/closing the modal.
Something like this will work:
handleTestConnectClick = () => {
this.setState(prevState => ({
...prevState,
isOpen: !prevState.isOpen
}));
}
here we use our previous state and with the ! operator we switch from true to false and vice versa
Update 2.0:
After taking a closer look at the Material UI documentation, I noticed that your dialog prop for setting the modal visibility is wrong. It should be open instead of isOpen.
import TestConnectDialog from './TestConnectDialog';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
//this.handleTestConnectClick = this.handleTestConnectClick.bind(this);
//this.handleCloseDialog = this.handleCloseDialog.bind(this);
// when using arrow functions you don't need to bind the this keyword
}
handleTestConnectClick = () => {
this.setState(prevState => ({
...prevState,
isOpen: !prevState.isOpen
}));
}
render() {
return (
<div className="section">
<Button className="connect-test-button"
// onClick={this.handleTestConnectClick}>
// change the onClick to the one below
onClick={ () => this.handleTestConnectClick() }
Test
</Button>
<TestConnectDialog isOpen={this.state.isOpen} handleTestConnectClick={this.handleTestConnectClick}/>
</div>
);
}
};
export default HomePage;
In TestConnectDialog component:
class TestConnectDialog extends React.Component {
render() {
return (
<Dialog
open={this.props.isOpen}
onClose={this.props.handleTestConnectClick}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleTestConnectClick} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
You're passing the props <TestConnectDialog isOpen={this.state.isOpen} /> but trying to read it with isopen={this.props.isopen}.
Change your code to this: isopen={this.props.isOpen}
Update TestComponent component as given
class TestConnectDialog extends React.Component {
render() {
const {isOpen, onOk} = this.props;
return (
<Dialog
isopen={isOpen}
onClose={this.props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleClose} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
In the homepage component why is isOpen destructured from the prop and initialised in state. You have to use one, using both is confusing, you are working with that on the state but passing the one from the prop
how to extract arrays in a state through React.createContext();
This is my state
constructor(props){
super(props)
this.state = {
sideDrawerOpen: false,
resultData:[
{id:"1",desc:"Dining",img:dining},
{id:"2",desc:"Wellness",img:wellness},
{id:"3",desc:"Activities",img:activities},
{id:"4",desc:"Dining",img:dining},
{id:"5",desc:"Wellness",img:wellness},
{id:"6",desc:"Activities",img:activities},
{id:"7",desc:"Activities",img:activities}
],
title:"lorem"
}
}
This is MyContext.provider
return (
<MyContext.Provider value={this.state}>
<Toolbar click={this.drawerButtonClickHandler} />
{sideDrawer}
{backdrop}
<Banner />
<HomeMenu data={this.state} />
<Merchants />
<Voucher />
<br />
</MyContext.Provider>
)
I tried looping but it didn't work but if I release data from a state that isn't an array I can do it.
class HomeMenu extends Component{
render(){
return(
<MyContext.Consumer>
{(context) => (
<p>{context.title}</p>
)}
</MyContext.Consumer>
)
}
}
According to react docs
the code below will re-render all consumers every time the Provider re-renders because a new object is always created for value
So i made a simple example to test this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
console.log('App');
return (
<>
<ThemeContext.Provider value={this.state.value}>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</>
);
}
handler=()=>{
this.forceUpdate()
}
}
const app = <App />;
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app,mountNode);
It seems that in every click, even though the reference is the same, toolbar component re-renders along with provider. So what is wrong here?
Writing the consumer as direct children of App will cause them to render when App component re-renders instead you must write your code as
const ThemeContext = React.createContext();
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
console.log('App');
return (
<React.Fragment>
<ThemeContext.Provider value={this.state.value}>
{this.props.children}
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</React.Fragment>
);
}
handler=()=>{
this.forceUpdate()
}
}
const app = (<App>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</App>)
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id="app"/>
try this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
handler(){
this.forceUpdate()
}
render() {
console.log('App');
return (
<div>
<ThemeContext.Provider value={this.state.value}>
<ThemeContext.Consumer>
{(value)=>( <Toolbar test={value}/>)}
</ThemeContext.Consumer>
</ThemeContext.Provider>
<button onClick={this.handler}>click me</button>
</div>
);
}
}
const app = <App />;
class Toolbar extends React.Component {
render() {
console.log('Toolbar');
return (
<div></div>
);
}
}
ReactDOM.render(app,mountNode);
I want to be able to update a child component property from both the parent and itself according to the last event.
For example:
class MyParent extends Component {
state ={
text:"";
}
render() {
return (
<View>
<MyChild text={this.state.text} />
<Button
onPress={()=>this.setState({text:"parent"})}
title="Update From Parent"
/>
</View>
);
}
}
class MyChild extends Component {
state ={
text:"";
}
componentWillReceiveProps(nextProps) {
if (nextProps.text!== this.state.text) {
this.setState({text:nextProps.text});
}
}
render() {
return (
<View>
{/* I want that the text field will be updated from the last event*/}
<Text>{this.state.text}</Text>
<Button
onPress={()=>this.setState({text:"child"})}
title="Update From Child"
/>
</View>
);
}
}
The issue is that componentWillReceiveProps is triggered each time the setState is called so the text property takes the value from the parent and not from the child.
How can I achive this result?
Thanks a lot
Elad
Manage your state through parent component and pass the function that will update the state of parent component in child component
class MyParent extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
updateParentState: (newState) => this.setState(newState)
}
}
render() {
let { text, updateParentState } = this.state;
return (
<View>
<MyChild data={{ text, updateParentState }} />
<Button
onPress={() => updateParentState({ text: "parent" })}
title="Update From Parent"
/>
</View>
);
}
}
class MyChild extends Component {
constructor(props) {
super(props);
this.state = {
text: props.data.text
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.text !== this.state.text) {
this.setState({ text: nextProps.data.text });
}
}
render() {
let { updateParentState } = this.props.data;
return (
<View>
<Text>{this.state.text}</Text>
<Button
onPress={() => updateParentState({ text: "child" })}
title="Update From Child"
/>
</View>
);
}
}
You are missing this. in the setState call in child. please check if this is not the issue.
<Button
onPress={()=>setState({text:"child"})}
title="Update From Child"
/>
should be
<Button
onPress={()=>this.setState({text:"child"})}
title="Update From Child"
/>
In my main component I can open a modal by clicking on an icon. The content of the modal is a separate component, which is calling a method.
If the method call is successful, I want to close the modal. But how can I do this?
Main component
class Example extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
<Modal trigger={ <Icon name='tags' /> } >
<Modal.Header>
<div>
<Header floated='left'>Title</Header>
<Button floated='right'>A Button</Button>
</div>
</Modal.Header>
<Modal.Content>
<ModalContent />
</Modal.Content>
</Modal>
</div>
)
}
}
Modal content
class ModalContent extends Component {
constructor(props) {
super(props)
this.state = {}
}
handleClick() {
method.call(
{ param },
(error, result) => {
if (result) {
// Now close the modal
}
}
);
}
render() {
return (
<Button onClick={this.handleClick} content='Save' />
)
}
}
You should add an onClose property to <Modal> element. See example below:
<Modal
trigger={<Button onClick={this.handleOpen}>Show Modal</Button>}
open={this.state.modalOpen}
onClose={this.handleClose}
>
Then you can add onClose function to a button in your modal. Full example from the docs:
https://react.semantic-ui.com/modules/modal#modal-example-controlled
Pass a onSuccess method as a props :
in the parent :
<ModalContent onSuccess={this.onModalSuccess}/>
in the child component :
handleClick() {
method.call(
{ param },
(error, result) => {
if (result) {
this.props.onSuccess()
}
}
);
}
In this way you keep your open/close logic in the parent component.
semantic-ui have property open. Just set true or false
class Example extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
open = () => this.setState({ open: true })
close = () => this.setState({ open: false })
render() {
return (
<div>
<Modal open={this.state.open} trigger={ <Icon name='tags' /> } >
<Modal.Header>
<div>
<Header floated='left'>Title</Header>
<Button floated='right'>A Button</Button>
</div>
</Modal.Header>
<Modal.Content>
<ModalContent />
</Modal.Content>
</Modal>
</div>
)
}
}