React: Why is element style not toggling with setState? - reactjs

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;

Related

ReadOnly state when I need to change it

I have the following code and I really need to be able to change the state however I am having issues when I try and do the following.
export default class Mediaplayer extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
station: null,
playButton: false,
muteButton: false,
};
}
render() {
const { station, playButton, muteButton } = this.state;
const handleMClick = (e) => {
// Event("Play Button", "Listner Hit Play", "PLAY_BUTTON");
console.log("clicking the play and pause button");
this.setState({ playButton: !playButton });
playButton
? document.getElementById("player").play()
: document.getElementById("player").pause();
};
return (
<i onClick={handleMClick}>
{playButton ? <PlayCircle size={60} /> : <PauseCircle size={60} />}
</i>
);
}
}
I am getting this state is ReadOnly.
setState() only takes effect after the whole eventHandler is
finished, this is called state batching.
Your this.setState({playButton:!playButton}) only run after handleMClick() is finished.
In other words, playButton === true will not available within your handleMClick() function.
On solution could be to put this:
playButton ? document.getElementById("player").play() : document.getElementById("player").pause()
Inside a componentDidUpdate() so it will take effect in the next render after your state is updated.
Direct dom manipulation is not a recommended way of doing things in react because you can always change dom element state according to your react component state or props.
I see your component is called media player but it doesn't have the #player inside it? Perhaps you could reconsider how you arranging the dom element.
Also try to use a functional component instead of class component. I will give an answer with a functional component.
MediaPlayer Component
import { useState } from 'react';
const MediaPlayer = props => {
const [play, setPlay] = useState(false);
const togglePlay = () => {
setPlay( !play );
}
return (
<i onClick={togglePlay}>
{!play ?
<PlayCircle size={60}/>
:
<PauseCircle size={60}/>}</i>
}
);
}

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

How to fix setState() gives the opposite value of checkbox?

This is my class component:
import React from 'react';
import { Jumbotron, Container, Label } from 'reactstrap';
import './Css.css';
class Settings extends React.Component {
state = {
isChecked: false,
}
isCheck(e) {
this.setState({
isChecked: e.target.checked
})
console.log(e.target.checked)
console.log(this.state.isChecked)
}
render() {
return (
<div className="margin">
<Jumbotron fluid>
<Container fluid>
<h1 className="display-3">Settings</h1>
<div className="groupbox">
<Label className="lead">This is a checkbox</Label>
<input class="apple-switch" type="checkbox" onChange={e => this.isCheck(e)} />
</div>
</Container>
</Jumbotron>
</div>
);
}
}
export default Settings;
The problem that I have is that the values that I get in the console are inversed.
So when the checkbox is unchecked e.target.checked is false but this.state.isChecked is true, and when the checkbox is checked e.target.checked is true and this.state.isChecked is false.
I don't know why I'm facing this issue.
Is there anything that I did wrong?
I tried also to store the e.target.checked to a const checked and then pass it to the setState() but it gives me the same thing.
Any help is appreciated.
The problem is purely a logging one. There is a delay between setting the value and seeing it updated on state. Try:
isCheck(e) {
console.log(e.target.checked);
this.setState({
isChecked: e.target.checked
}, () => {
console.log(this.state.isChecked);
});
}
If you need to do something after state has been updated, then put it into a callback function inside the setState call, as above. The reason for this is that setState calls render (that is its main purpose), and React will do that asynchronously, when it's ready.
According to the doc
Think of setState() as a request rather than an immediate command to update the component
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied
So setState() here is asynchronous, so in your case, to see the effect, just use a callback (here I use arrow function to maintain the context of this)
isCheck(e) {
this.setState(
{
isChecked: e.target.checked,
},
() => {
console.log(e.target.checked)
console.log(this.state.isChecked)
}
)
}

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

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.

Material UI TextField React Component Conditional style don't trigger textField render

React Component :
class Info extends Component {
constructor() {
this.state = {
editAccount: false
}
this.changeTrue = this.changeTrue.bind(this);
this.changeFalse = this.changeFalse.bind(this);
}
changeTrue() {
this.setState({ediAccount: true}
}
changeFalse() {
this.setState({ediAccount: false}
}
render() {
const specific_style = this.state.editAccount ? { background: 'red'} : { background : 'yellow' }
return (
<div>
<TextField
id='first_name'
inputStyle={ specific_style}
value={this.state.first_name}
/>
<button onClick={this.changeTrue}>Click True</button>
<button onClick={this.changeFalse}>Click False</button>
</div>
)
}
}
Having this component and editAccount having the state changed doesn't rerender apply the style changes? Doesn't rerender the TextField ? Anybody knows why ?
State Updates May Be Asynchronous
React may batch multiple setState()
calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
When updating the state based on the current state always use a callback in the call to setState(). The callback gets the previous state and returns the next state. This is because react may batch multiple calls to setState() thus not using a callback will override previous calls:
this.setState(prevState => ({editAccount: !prevState.editAccount)});
Also in your object that contains the styles you used variables (which you did not define) instead of strings:
const specific_style = this.state.editAccount ? { background: red /* red is not defined*/} : { background : yellow /* same here */ };
It should probably be:
const specific_style = this.state.editAccount ? { background: 'red' } : { background : 'yellow' };
Objects can't be written like css classes.
The fully working code has to look about like this:
class Info extends Component {
constructor(props) {
super(props);
this.state = {
editAccount: false
};
this.changeStyle = this.changeStyle.bind(this);
}
changeStyle() {
this.setState(state => ({editAccount: !state.editAccount}));
}
render() {
const specific_style = this.state.editAccount ? { background: 'red' } : { background: 'yellow' };
return (
<div>
<TextField
id="first_name"
inputStyle={specific_style}
/>
<button onClick={this.changeStyle}>Toggle red/yellow</button>
</div>
);
}
}
See this working codesandbox example.
It appears you forgot to put '<' and '/>' within the return of your render function. Two of us made edits to resolve this but perhaps thats also the issue
You may need to use a lifecycle method like componentWillReceiveProps to force a re-render. Also - format your code
Where / How is editAccount defined ? It should come from the state or the props to trigger the re-render.
If the render() method isn't affected by the props / state changes, it isn't triggered.

Resources