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

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.

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

React setstate on big and small states

I have a state like this :
{
textfield: '',
data: [] //huge, used to render elements within the render()
}
When I want to update the textfield value (simple text input), I use this.setState({ textfield: newValue });. The problem is that there is some lag when I write a character in the field because it is re-rendering everything.
Is using shouldComponentUpdate() and deeply check my data object the only way to avoid re-rendering everything? Or is there a better/more efficient way?
Thanks
Am guessing its rerendering the entire component due to the state change on every key.
you could isolate your input element in a separate stateful component, hence only triggering a re-render on itself and not on your entire app.
So something like:
class App extends Component {
render() {
return (
<div>
...
<MyInput />
...
</div>
);
}
}
class MyInput extends Component {
constructor() {
super();
this.state = {textfield: ""};
}
update = (e) => {
this.setState({textfield: e.target.value});
}
render() {
return (
<input onChange={this.update} value={this.state.textfield} />
);
}
}

React: Why is element style not toggling with setState?

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;

Tabs only mount Tab content on the first time it becomes active

I would like to load the tab content only on the first time it becomes active, after that the content stays in the DOM
This is what I have
<Tabs defaultActiveKey={1} animation={false} id="my-tabs" mountOnEnter unmountOnExit>
<Tab eventKey={1}>
<div>content1</div>
</Tab>
<Tab eventKey={2}>
<div>content1</div>
</Tab>
</Tabs>
it works fine, but there is a lag between switching tabs, since the content I have is quite large and I would like to render it only once, on the first time the tab becomes active.
Is there a way to achieve that? I'm using react-bootstrap 0.30.10
UPDATE:
apparently mountOnEnter must be used with animation, otherwise it will not work as intended. I made the change and it works fine now
Old answer:
so I have come up with this wrapping component as follow
class TabsLazyLoad extends Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
this.handleSelect = this.handleSelect.bind(this);
}
getInitialState() {
return {
key: this.props.key || this.props.defaultActiveKey,
rendered: [],
};
}
addRenderedTab(key) {
const newState = _.cloneDeep(this.state);
newState.rendered.push(key);
this.setState(newState);
}
handleSelect(key) {
this.setState({ key });
}
render() {
return (
<Tabs activeKey={this.state.key} onSelect={this.handleSelect} {...this.props}>
{_.map(this.props.children, (tabComponent) => {
if (_.includes(this.state.rendered, tabComponent.props.eventKey)) {
return tabComponent;
}
if (tabComponent.props.eventKey === this.state.key) {
this.addRenderedTab(this.state.key);
}
// if it's not rendered, return an empty tab
const emptyTab = _.cloneDeep(tabComponent);
emptyTab.props.children = null;
return emptyTab;
})}
</Tabs>
);
}
}
TabsLazyLoad.propTypes = Tabs.propTypes;
It seems to be working fine, but I reckon this is a bit hacky, but it's the best I can come up with for now.
It sounds like a good use case for the "Avoid Reconciliation" option that React provides.
Here's a link to the relevant section in the documentation.
Essentially, there's a lifecycle event called shouldComponentUpdate that defaults to true. When you change it to false, it tells React not to run the component through the standard Reconciliation process (i.e. the "diff" checks).
Like with any lifecycle method, you can create a conditional statement for it.
For a component that should be made completely static after its first render, this is really all you need:
class YourComponent extends React.Component {
...
shouldComponentUpdate() {
return false;
}
...
}
However, for a more general use case, you'd want to write a conditional statement based on the props and/or the state of the component:
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
// Your state
};
}
shouldComponentUpdate(nextProps, nextState) {
// A conditional statement to determine whether
// this component should check for updates or not
}
render () {
return (
<div>
{/* Your JSX*/}
</div>
)
}
I don't use React Boostrap but I guess it's based on the Component design,
example, the rendered content used TabIndex state. Take a closer look at this sample code:
renderActiveTabContent() {
const { children } = this.props
const { activeTabIndex } = this.state
if (children[activeTabIndex]) {
return children[activeTabIndex].props.children
}
}
So the content component render every time Tab state is indexed.
You could use https://github.com/reactjs/react-tabs for your solution other wise take a look of those codes to write a simple one, the Component is rendered once and show/hide state via display: style attribute.
Hope it's help.

Resources