setState being called too late - javascript

I am trying to make a text box auto focus.
However, I the setState is being called too late it seems.
It is being called within Popup.show. I created a button to console.log the state, and it does seem to be set to true but it must happen too late.
How can I get setState to be called as the Popup.show happens?
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
focused: false,
};
}
onClick = (event) => {
console.log('Says focussed FALSE', this.state.focused)
this.setState({ focused:true });
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>,
console.log('Says focussed FALSE still', this.state.focused),
{ animationType: 'slide-up'});
};
checkState = (e) =>{
console.log(this.state)
}
render() {
return (
<div style={{ padding: '0.15rem' }}>
<Button onClick={this.onClick.bind(this)}>Open & Focus</Button>
</div>);
}
}

Always remember that setState won't execute immediately. If you want Popup.show() after setState, you can use a callback:
this.setState({ focused: true }, () => {
Popup.show(...)
})
And you are already using arrow functions, you don't need the .bind(this) in your render function.

setState doesn't immediate set the state
From: https://facebook.github.io/react/docs/react-component.html#setstate
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
Changing your setState to something like
this.setState({ focused: true }, () => {
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>)
});

Related

ReactJS: Hiding a text produces an error: Maximum update depth exceeded

I just cant hide my text (Header) using a button in a class form. I try this code below:
constructor(props){
super(props)
this.state = {
showHeader: true,
}
}
And I render the state above using setState:
render () {
return (
<div>
{this.state.showHeader && <Header /> }
<button onClick={ this.setState({showHeader: false})} >Hide</button>
</div>
I know this is a stupid question but I cant help myself because Im a totally beginner. But I did this right using function and I just want try to convert it using a class. This is what I did using function:
const [show, setShow] = React.useState(true);
const hideHeader = () => {
setShow(!show)
}
And return this:
return (
<div>
{show && <Header />}
<button onClick={hideHeader}>Hide Header</button>
</div>
)
Right now you're calling setState() in your render function. That's going to cause problems because setState causes your render method to be called, and if your render method calls setState directly, you get caught in a loop.
What you need to do is call it in an event handler instead:
// bad
onClick={this.setState({showHeader: false})}
// good
onClick={() => this.setState({showHeader: false})}
So your button should look like this:
<button onClick={() => this.setState({showHeader: false})} >Hide</button>
From the docs:
The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.

setState is not working with multiple onClick function

I have weird issue, and been trying to debug for hours, still can't not find out why.
I have a button onClick, to handle other 2 function, some weird reason the setState one wouldn't work if I have 2 function, if I just keep one function, setState will work, but I need both function.
Here's my code
(And when I reproduce the issue here, it seems work, but the sample it's not update API, it's just change the state)
Thank you for the help!
class RecipeReviewCard extends React.Component {
constructor(props) {
super();
this.state = {
expanded: false,
in: false,
text: false
};
}
handleClick = () => {
this.setState(state => ({ expanded: !state.expanded }));
};
/* If I comment out this one, first setState will work */
handleClick = e => {
this.setState({ in: !this.state.in });
/* This come from parent props to update API */
this.props.updateThing(e.target.value)
};
<Button
className={classes.button}
onClick={e => this.handleClick()}
>
In your constructor, you should call super(props);
see: https://overreacted.io/why-do-we-write-super-props/
In your Button - you are not passing the event object to your event handler.
<Button
className={classes.button}
onClick={e => this.handleClick(e)} // note the e here.
>
Since your handleClick function is already bound to your component instance, you should just provide it to onClick handler directly without writing that arrow function, like so:
<Button
className={classes.button}
onClick={this.handleClick}
>

Save textarea value to div with ReactJs

I'm only trying to deal with the React, so my question may seem very simple, but still I'm stuck on it.
I have two blocks (div.user-data__row) in which there are some values. I change the state of the component (handleChange function), the state in which these blocks are replaced by text fields (textarea.text), and I want when I click on the save button and call saveChange function, the value from each text field is taken and passed to the blocks (1st textarea to 1st block, etc).
I found examples of solving a similar case using the ref attribute, but later read that this is no longer an actual solution and so no one does. Please help me find the actual implementation path.
class UserData extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: true,
};
this.handleChange = this.handleChange.bind(this);
this.saveChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(() => ({
edit: !this.state.edit,
}));
}
saveChange() {
this.setState(() => ({
edit: false
}))
}
render() {
if (this.state.edit) {
return (
<div className="user-data">
<div className="user-data__row">{this.props.userData.name}</div>
<div className="user-data__row">{this.props.userData.email}</div>
<button onClick={ this.handleChange }>Edit</button>
</div>
);
} else {
return (
<div className="user-data">
<textarea className="text" defaultValue={this.props.userData.name}></textarea>
<textarea className="text" defaultValue={this.props.userData.email}></textarea>
<button onClick={ this.saveChange }>Save</button>
</div>
)
}
}
}
Because props are read-only & because userData (email+name) can be changed inside the component , you have to populate props values in the state, then manage the state after that will be enough.
Also , you will need to convert your textarea from uncontrolled component to controlled component by:
Using value instead of defaultValue
Implementing onChange with setState of that value as handler .
Value of textarea should be read from state not props.
If props of <UserData /> may be updated from outside throughout its lifecycle , you will need componentWillReceiveProps later on.
Also you have a typo if (!this.state.edit) { and not if (this.state.edit) { .
class UserData extends React.Component {
state = {
edit: true,
userDataName: this.props.userData.name, // Populate props values
userDataEmail: this.props.userData.email, // Populate props values
};
handleChange = () => {
this.setState((state) => ({
edit: !state.edit,
}));
}
saveChange =() => {
this.setState(() => ({
edit: false
}))
}
render() {
if (!this.state.edit) {
return (
<div className="user-data">
<div className="user-data__row">{this.state.userDataName}</div>
<div className="user-data__row">{this.state.userDataEmail}</div>
<button onClick={ this.handleChange }>Edit</button>
</div>
);
} else {
return (
<div className="user-data">
<textarea className="text" value={this.state.userDataName} onChange={(e) => this.setState({userDataName: e.target.value})}></textarea>
<textarea className="text" value={this.state.userDataEmail} onChange={(e) => this.setState({userDataEmail: e.target.value})}></textarea>
<button onClick={ this.saveChange }>Save</button>
</div>
)
}
}
}
ReactDOM.render(<UserData userData={{name: 'Abdennoor', email: 'abc#mail.com'}} /> , document.querySelector('.app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div class="app" />
You're receiving values for <div> from this.props, it means that these values should came from some external source and passed to your component. A way of passing these values to component's props is out of scope of this question, it can be implemented in a very different ways. Usually it came either from parent component or from some connected store.
You need to obtain values from your <textarea> form fields, it can be done directly (using ref) or by using some third-party library that provides form handling. Then these values needs to be stored (and obtained) either directly from component's state or from external source (via props).
Unfortunately scope of your question is too broad to be able to give more precise answer, but hope that this information will lead you to some kind of solution.
You can also use contentEditable, which it will allow you to edit the content of the div.
class UserData extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: true
};
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(() => ({
edit: !this.state.edit
}));
}
render() {
const { edit } = this.state;
return (
<div className="user-data">
<div className="user-data__row" contentEditable={edit}>{this.props.userData.name}</div>
<div className="user-data__row" contentEditable={edit}>{this.props.userData.email}</div>
<button onClick={this.handleChange}>
{edit ? Save : Edit}
</button>
</div>
)
}
}

reactjs setState not changing state

I have a function(onClickAddSection), when its called, it should set the state to empty string but it doesn't do that at all.
Please take a look at the code and tell me what im doing wrong thank you.
class AddNewSectionForm extends Component {
constructor(props) {
super(props);
this.state = {
sectionName: '',
validation:false
};
this.onSectionNameChange = this.onSectionNameChange.bind(this);
this.onClickAddSection = this.onClickAddSection.bind(this);
}
onSectionNameChange(event) {
if(this.state.validation==false){
this.setState({validation:true});
}
this.setState({sectionName: event.target.value});
}
onClickAddSection(event) {
event.preventDefault();
this.props.saveSection(this.state.sectionName);
this.setState({sectionName:'',validation:false});
}
render() {
return (
<div>
<TextInput name="newSection" label="Section Name :"
onChange={this.onSectionNameChange}
value={this.state.sectionName}
error = {this.state.validation==true&& this.state.sectionName.length==0?'Enter Section Name':''}/>
<AddCloseButtons add = {this.onClickAddSection}
close = {this.props.closeCharm}
/>
</div>
);
}
}
the onClickAddSection function doesn't set the sectionName back to ''.
AddCloseButton:
const AddCloseButtons = ({add,close}) => {
return (
<div className="form-group">
<button className="btn btn-primary" style={{width: '40%', border:'solid black 1px'}} onClick={add}>Add</button>
<button className="btn btn-secondary" style={{width: '40%', float: 'right',border:'solid black 1px'}} onClick={close}>Close</button>
</div>);
};
Are you using Redux? if so, this.props.saveSection makes any Ajax Call?. If so, could be that you update the local state with setState and then your Ajax response re updates it the received value?
Just try and see the functional version of setState.
this.setState(function (state, props) {
return {
sectionName:'',
validation:false
}
});
It seems that when you click the button, onSectionNameChange and onClickAddSection happen at the same time and will have conflict (because the name is set to blank means its name is being changed ^^), so how about not setting state for sectionName in onSectionNameChange:
onSectionNameChange(event) {
if(this.state.validation==false){
this.setState({validation:true});
}
//this.setState({sectionName: event.target.value});
}
I guess so, please post here some errors if any, thanks

Focusing div elements with React

Is it possible to focus div (or any other elements) using the focus() method?
I've set a tabIndex to a div element:
<div ref="dropdown" tabIndex="1"></div>
And I can see it gets focused when I click on it, however, I'm trying to dynamically focus the element like this:
setActive(state) {
ReactDOM.findDOMNode(this.refs.dropdown).focus();
}
Or like this:
this.refs.dropdown.focus();
But the component doesn't get focus when the event is triggered. How can I do this? Is there any other (not input) element I can use for this?
EDIT:
Well, It seems this it actually possible to do: https://jsfiddle.net/69z2wepo/54201/
But it is not working for me, this is my full code:
class ColorPicker extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false,
value: ""
};
}
selectItem(color) {
this.setState({ value: color, active: false });
}
setActive(state) {
this.setState({ active: state });
this.refs.dropdown.focus();
}
render() {
const { colors, styles, inputName } = this.props;
const pickerClasses = classNames('colorpicker-dropdown', { 'active': this.state.active });
const colorFields = colors.map((color, index) => {
const colorClasses = classNames('colorpicker-item', [`colorpicker-item-${color}`]);
return (
<div onClick={() => { this.selectItem(color) }} key={index} className="colorpicker-item-container">
<div className={colorClasses}></div>
</div>
);
});
return (
<div className="colorpicker">
<input type="text" className={styles} name={inputName} ref="component" value={this.state.value} onFocus={() => { this.setActive(true) }} />
<div onBlur={() => this.setActive(false) } onFocus={() => console.log('focus')} tabIndex="1" ref="dropdown" className={pickerClasses}>
{colorFields}
</div>
</div>
);
}
}
React redraws the component every time you set the state, meaning that the component loses focus. In this kind of instances it is convenient to use the componentDidUpdate or componentDidMount methods if you want to focus the element based on a prop, or state element.
Keep in mind that as per React Lifecycle documentation, componentDidMount will only happen after rendering the component for the first time on the screen, and in this call componentDidUpdate will not occur, then for each new setState, forceUpdate call or the component receiving new props the componentDidUpdate call will occur.
componentDidMount() {
this.focusDiv();
},
componentDidUpdate() {
if(this.state.active)
this.focusDiv();
},
focusDiv() {
ReactDOM.findDOMNode(this.refs.theDiv).focus();
}
Here is a JS fiddle you can play around with.
This is the problem:
this.setState({ active: state });
this.refs.component.focus();
Set state is rerendering your component and the call is asynchronous, so you are focusing, it's just immediately rerendering after it focuses and you lose focus. Try using the setState callback
this.setState({ active: state }, () => {
this.refs.component.focus();
});
A little late to answer but the reason why your event handler is not working is probably because you are not binding your functions and so 'this' used inside the function would be undefined when you pass it as eg: "this.selectItem(color)"
In the constructor do:
this.selectItem = this.selectItem.bind(this)
this.setActive = this.setActive.bind(this)
This worked in my case
render: function(){
if(this.props.edit){
setTimeout(()=>{ this.divElement.focus() },0)
}
return <div ref={ divElement => this.divElement = divElement}
contentEditable={props.edit}/>
}

Categories