Change status of state variable in Child Component of react.js - reactjs

I have a parent component like below. I have a button here named View.
class DataTable extends Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false,
};
this.view = this.view.bind(this);
}
view() {
this.setState({ modalOpen: true });
}
render() {
return (
<div>
<button className="mini ui button" onClick={this.view}>
<i className="user icon"></i>
View
</button>
<ModalBody modelStatus = {this.state.modalOpen}/>
</div>
)
}
}
I have a child component like below
class ModalBody extends Component {
state = { modalchildOpen: false }
componentDidMount() {
if(this.props.modelStatus) {
this.setState({ modalchildOpen: true })
console.log('yes')
}
else {
this.setState({ modalchildOpen: false })
console.log('no')
}
}
render() {
return (
<div>
<Modal open={this.state.modalchildOpen}/>
</div>
)
}
}
I would like to change status of modalchildOpenfrom false to true while clicking on Button View. In another action I would like to change status of modalchildOpenfrom true to false in the child component.

I agree with #lustoykov about how you would normally set the modal open/closed value through state. However, if you want to update the state based on props passed down from the parent, what you’re probably looking for is the componentWillReceiveProps life cycle method. This method runs anytime your child component receives props and you can compare the old props to the new props. You can then set the state inside that function.
Reference this link:
https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
Please note, there is newer version of this life cycle method called getDerivedStateFromProps. Be sure to check your versioning and see if you can use the new method as the old one will eventually become deprecated.

I solved the issue using below code.
componentWillReceiveProps(nextProps){
this.setState({
modalchildOpen: nextProps.modelStatus,
})
}
Thanks all.

Related

setState conflicts with getDerivedStateFromProps

import React, { Component } from "react";
export interface MyComponentProps {
show: boolean;
}
export interface MyComponentState {
show: boolean;
}
export default class App extends Component<MyComponentProps, MyComponentState> {
static defaultProps = {
show: true
};
static getDerivedStateFromProps(props: MyComponentProps) {
console.log("getDerivedStateFromProps: ", props);
if ("show" in props) {
return { show: props.show };
}
return null;
}
constructor(props: MyComponentProps) {
super(props);
this.state = {
show: props.show
};
}
onClick() {
this.setState({ show: false });
}
render() {
const { show } = this.state;
return (
<div>
{show ? "teresa teng" : ""}
<button type="button" onClick={() => this.onClick()}>
toggle
</button>
</div>
);
}
}
getDerivedStateFromProps() static method will be executed after setState(). So I click the button to try to change the value of state.show to false, but the getDerivedStateFromProps() method will change state.show to true. So the text will always be visible.
getDerivedStateFromProps intends to use the props passed in by the parent component to update the state.
How can I solve this? Playground codesandbox.
getDerviedStateFromProps is bound to run after every prop and state change. This was not an actual design but this change in functionality was introduced in React version 16.4 (if I remember correctly).
Now, if you want to update the local show i.e. your state on the basis of your props, you can:
Pass a callback which updates show for you in the parent component and then use the new prop value.(As mentioned by #jonrsharpe in the comments).
You can also make use of a key prop which tells your component to completely unmount and mount itself in case of a key change. This will lead to the state getting reset based on the value of the props.
For ex,
<App show={this.state.show}
key={this.state.show}/>
Example CodeSandBox

React Toggle Not Changing Boolean Value

I have a simple component using a toggle in Nextjs. I would like to change the content of the button based on whether 'isOpen' is true or false. However, the console shows isOpen is always set to false. Here's the code:
export class Hamburger extends Component {
constructor(props) {
super(props);
this.state = { isOpen: false }
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState = ({isOpen: !this.state.isOpen})
console.log(this.state.isOpen)
}
render() {
return (
<button onClick={this.toggle} type="button">
{this.state.isOpen ? <Component1 /> : <Component2 /> }
</button>
)
}
}
export default Hamburger
I've researched answers on SE all morning but I still can't understand why isOpen is not changing.
Any help would be very much appreciated!! Cheers.
If your new state depends on your previous state, call setState with a callback like this:
this.setState(prevState => ({
isOpen: !prevState.isOpen
}));
If your new state update depends on the previous state, always use the functional form of 'setState' which accepts as argument a function that returns a new state.
this.setState(prevState => ({
check: !prevState.check
}));
you can check the default value first
this.setState = ({isOpen: this.state.isOpen ? false : true })

How to handle multiple material-UI Popovers [React.js]?

My app has multiple Popover components, I know how to handle the state of one Popover component, using something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = { pop_open: false };
}
handleProfileDropDown(e) {
e.preventDefault();
this.setState({
pop_open: !this.state.pop_open,
anchorEl: e.currentTarget,
});
}
handleRequestClose() {
this.setState({
pop_open: false,
});
};
render() {
return (
<div>
<button type="submit" onClick={this.handleProfileDropDown.bind(this)} >My Customized PopOver</button>
<Popover
open={this.state.pop_open}
anchorEl={this.state.anchorEl}
onRequestClose={this.handleRequestClose.bind(this)}
>
{"content"}
</Popover>
</div>
);
}
}
But for more than one Popover, I do not know how to do that, should I create a state for each Popover? Sorry for the question but I am new to the frontend world.
note: kindly do not use hooks in your answer.
An internal state is a good option when only the Component is going to modify it. It keeps the logic simple and inside the same block of code. On the other hand managing the state from outside of the Component lets other components read its values and modify them. This is a common approach when using Redux or Context, where there is a global app state. This state is meant for properties that several Components need to read/write to.
Which to use when is a design decision and depends on each situation. In my opinion each Component should handle its own state when possible. For example, when values are only going to be modified by it, or a children Component. Having an external state makes sense when multiple Components are going to read or modify it, or when the state values need to be passed several levels deep in the hierarchy.
In the example you propose I can see that the Popover is working with an internal state. This can work and you can use the Component several times and it will carry all the logic inside. If you rename the Components you can see more easily what I mean. I dont know exactly how the Component with the button works but this is to make the explanation clear:
class Popover extends Component {
constructor(props) {
super(props);
this.state = { is_open: false };
}
open = () => {
this.setState({
is_open: true
});
}
close = () => {
this.setState({
is_open: false
});
}
toggle = () => {
this.setState(prevState => ({
is_open: !prevState.is_open
}));
}
render() {
return (
<div>
<button onClick={this.toggle}>
Open
</button>
{this.state.is_open && <PopoverContent />}
</div>
);
}
}
If you need further explanation or something is not clear, let me know.

How do I limit the user to only selecting one component?

I have the following code that simply constructs blocks for our products and the selected state allows the component to be selected and unselected. How can I figure out which of these components are selected and limit the user to only selecting one at a time. This is ReactJS code
import React from 'react';
export default class singleTile extends React.Component{
constructor(props){
super(props);
this.title = this.props.title;
this.desc = this.props.desc;
this.svg = this.props.svg;
this.id = this.props.id;
this.state = {
selected: false
}
}
selectIndustry = (event) => {
console.log(event.currentTarget.id);
if(this.state.selected === false){
this.setState({
selected:true
})
}
else{
this.setState({
selected:false
})
}
}
render(){
return(
<div id={this.id} onClick={this.selectIndustry}className={this.state.selected ? 'activated': ''}>
<div className="icon-container" >
<div>
{/*?xml version="1.0" encoding="UTF-8"?*/}
{ this.props.svg }
</div>
</div>
<div className="text-container">
<h2>{this.title}</h2>
<span>{this.desc}</span>
</div>
</div>
)
}
}
You need to manage the state of the SingleTile components in the parent component. What i would do is pass two props to the SingleTile components. A onClick prop which accepts a function and a isSelected prop that accepts a boolean. Your parent component would look something like this.
IndustrySelector.js
import React from 'react';
const tileData = [{ id: 1, title: 'foo' }, { id: 2, title: 'bar' }];
class IndustrySelector extends Component {
constructor(props) {
super(props);
this.state = { selectedIndustry: null };
}
selectIndustry(id) {
this.setState({ selectedIndustry: id });
}
isIndustrySelected(id) {
return id === this.state.selectedIndustry;
}
render() {
return (
<div>
{tileData.map((data, key) => (
<SingleTile
key={key}
{...data}
onClick={() => this.selectIndustry(data.id)}
isSelected={this.isIndustrySelected(data.id)}
/>
))}
</div>
);
}
}
The way this works is as follows.
1. Triggering the onClick handler
When a user clicks on an element in SingleTile which triggers the function from the onClick prop, this.selectIndustry in the parent component will be called with the id from the SingleTile component.
Please note that in this example, the id is remembered through a
closure. You could also pass the id as an argument to the function of
the onClick prop.
2. Setting the state in the parent component
When this.selectIndustry is called it changes the selectedIndustry key of the parent component state.
3. Updating the isSelected values form the SIngleTile components
React will automatically re-render the SingleTile components when the state of the parent component changes. By calling this.isIndustrySelected with the id of the SingleTile component, we compare the id with the id that we have stored in the state. This will thus only be equal for the SingleTile that has been clicked for the last time.
Can you post your parent component code?
It's not so important, but you can save some time by using this ES6 feature:
constructor(props){
super(props);
const {title, desc, svg, id, state} = this.props;
this.state = {
selected: false
}
}

Is it good practice to add an event in props.children, to set state in the parent, to set state in a child component

I want to build a control like this...
<ClickLabelEdit labelText='sandwich'>
<input type='text' value='sandwich' />
<button onclick={this.closeForm} />
</ClickLabelEdit>
ClickLabelEdit would have two states; show a readonly label and show an editable form for that label. Click the label and it displays the edit form. The edit form details would be defined in a property or as children so we could include any type of markup here. Inside the edit form we will need a hook to tell the control to hide the edit form and display the label again.
This ClickLabelEdit would be a reusable component...used in multiple places around my application. The data used in the label and the form could be taken from a redux store. I think the state (or props) that define whether the label or edit form should be displayed doesn't belong in the redux store because that state/prop would have to be duplicated everywhere I use this control which could become messy.
So...
where to put the showEditForm prop/state
where to put the onclick label function that alters the showEditForm prop/state
how to close the edit form and show the label again (this would be triggered from the edit form)
I have got this to work but my main question is it acceptable to use state to trigger events like I have done below, ie
onclick in children props -> sets state in parent component -> updates state in child
ClickLabelEdit.js
class ClickLabelEdit extends Component {
constructor(props) {
super(props)
this.state = { showChildren: this.props.showChildren }
}
componentWillReceiveProps(nextProps) {
if (nextProps.closeForm === true) {
this.setState({ showChildren: false });
}
}
onLabelClick() {
this.setState({ showChildren: true })
}
render() {
return (
<div>
{
this.state.showChildren ?
this.props.children
:
<div onClick={this.onLabelClick.bind(this)}>
{this.props.labelValue}
</div>
}
</div>
)
}
}
export default ClickLabelEdit
usage
class ManualTester extends Component {
constructor(props) {
super(props)
this.state = { value: 'sandwich', closeForm: false }
}
onSave() {
//persist data
this.setState({ closeForm: true })
}
render() {
return (
<div style={{ width: 250 }}>
<ClickTextEdit labelValue={this.state.value} closeForm={this.state.closeForm}>
<input type='text' value={this.state.value} />
<button onClick={this.onSave.bind(this)}>close</button>
</ClickTextEdit>
</div>
)
}
}
export default ManualTester

Resources