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 })
Related
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
All code implements adding or removing classes(toggle).
I commented strings.
class Example extends React.Component {
state = {
isActive: false,
};
handleClick = () => {
this.setState(state => ({ isActive: !state.isActive })); // stirng №1
};
render() {
const { isActive } = this.state; // stirng №2
return (
<div>
<button onClick={this.handleClick}>Try it</button> //stirng №3
<div className={isActive ? 'mystyle' : ''}> //stirng №4
This is a DIV element.
</div>
</div>
);
}
}
How to read (decipher) this strings? Not only explanations of what they do, but how they are read
handleClick = () => {
this.setState(state => ({ isActive: !state.isActive })); // stirng №1
};
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
This is using the "updater function form", it executes a callback function with how the state should be modified. In this case, it's setting "isActive" to the inverse of the current state value of "isActive".
It's also using a short form of Arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions Read up on that there
const { isActive } = this.state; // stirng №2
This is using destructuring to "unpack" the "isActive" value from the state (the state is an object). So instead of accesing the value using "this.state.isActive", now you can just use "isActive".
<button onClick={this.handleClick}>Try it</button> //stirng №3
"onClick" is a Javascript event handler, basically it says that "when this button is clicked, execute this function". The function being "handleClick" defined above that toggles the "isActive" state.
Hope that helps!
Just wondering how can I add a background color for a active item using styed component? pretty much works like jQuery's add/remove class.
I have defined a active boolean variable in my state like this:
constructor(props) {
super(props);
this.state = {
placeholder: '',
active: false,
};
this.handleClick = this.handleClick.bind(this);
this.handleImageError = this.handleImageError.bind(this);
}
and a handleClick function can true active to true like this:
handleClick() {
this.setState({ active: true });
}
in my render function I have HTML like this:
<MemberStyled className="member-item" active={active} onClick={this.handleClick}>
<MemberStyled.avatar src={imgSource || placeholder} onError={this.handleImageError} />
<MemberStyled.user>{name}</MemberStyled.user>
</MemberStyled>
in member.styles.js file, I have style like this:
const MemberStyled = styled.li`
background-color: ${props => props.active ? red : 'transparent'};
`;
Right now, all clicked items will change the color to red. How can I make only actived item to have red background in styled component?
Thank you so much!
So all member-items look to be part of a higher parent component. You should push out the active state to this parent. Then give each member-item a unique prop (index, or id should do). Then while calling the parent's handleClick, send it the unique number and the parent should store that instead of a boolean. Finally, in parent's render, compare the unique number prop value with active state value and send that as active boolean prop to member-item. Use this boolean in member-item's render.
// in Parent Component
handleClick(activeKey){
this.setState({activeKey});
}
isActive(key){
const {activeKey} = this.state;
return key === activeKey;
}
render(){
return this.memberItems.map((index, itemData) => <MemberStyled active={this.isActive(index)} itemKey={index} handleClick={this.handleClick.bind(this, index)} />)
}
Best way for me is to save the index of your active element in the parentcomponent:
constructor(props) {
super(props);
this.state = {
placeholder: '',
active: -1,
};
this.handleClick = this.handleClick.bind(this);
this.handleImageError = this.handleImageError.bind(this);
}
onClick(e, index) {
this.setState({
active: index,
});
}
render() {
const { active } = this.state;
return this.memberItems.map((itemData, index) => <MemberStyled active={index === active} itemKey={index} handleClick={() => handleClick(this, index)} />)
}
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.
It's just a simple toggle mechanism:
Onclick of the element is to toggle a border color change by responding to state change. It changes the color once! But won't toggle back to original color.
(I've experimented with so many variations of the functionality, read/reread React docs on state, setState's asynchronous/batch change functionality, and combed SO again-and-again.)
Can someone help me find a solution?
Thanks in advance!
import React, { Component } from 'react';
class Button extends Component {
constructor(props) {
super(props);
this.state = {
active: false,
}
this.updateActive = this.updateActive.bind(this);
}
updateActive(){
this.setState(function(){
this.state.active = !this.state.active;
{ return this.state.active; }
});
}
render(){
return (
<div className="seq_btn" onClick={this.updateActive} style={ {borderColor: this.state.active ? 'black' : 'rgb(193, 255, 112)' }}></div>
)
}
}
export default Button;
Because your return syntax is incorrect:
this.setState(function(){
this.state.active = !this.state.active;
{ return this.state.active; }
});
This should be:
this.setState(function(){
return { active: !this.state.active };
});
However, you don't need to use the callback here at all. You should just setState with the new data.
this.setState({ active: !this.state.active });
As a matter of good habit, you should never mutate state in any form that isn't directly performed with setState
Even using
this.state.active = !this.state.active
is bad form and is most likely your issue.
Instead consider
this.setState({ active: !this.state.active });
Also understand that setState's can be batched for processing later, they are not always immediately executed.
setState() does not always immediately update the component. It may batch or defer the update until later.
https://reactjs.org/docs/react-component.html#setstate
As noted below, a functional component would serve the same purpose without the overhead of lifecycle methods
import React from "react";
const Button = ({ active, clickAction }) => (
<div onClick={clickAction} className={{ borderColor: active ? "green": "purple"}}>
</div>);
export default Button;