React invoking function calling setState passed in props of a stateless component - javascript

I am working on filter component being made with React.JS.
A few of those filtering components are embedded in a panel (div). The wrapping parts of the panel look like the below:
Initiative <a onClick={() => {this.setState({ showInitiatives: true })}} className="milestone-list-link" href="#initiative">
{this.state.selectedInitiative}
<InitiativeSelector
initiatives={initiatives || {}}
show={this.state.showInitiatives}
selector={
() => {
console.log('called selector state:', this.state)
this.setState({ showInitiatives: false })
}
}
/>
<FilterIcon />
</a>
My <InitiativeSelector /> looks like the below:
const InitiativeSelector = ({initiatives, show, selector}) => {
return (show) ? (
<div className="list-filter">
<div>
<input placeholder="Search..." />
</div>
<ul>
{
Object.values(initiatives).map((initiative, i) => <li onClick={() => {selector()}} key={i}>{initiative.name}</li> )
}
</ul>
</div>
) : null
}
When I run this, my selector does get called. Hence, I see state printed to the console. However, this.setState({ showInitiatives: false }) does not seem to do anything. My modal does not hide, and the second (etc) time I click on the <li>, showInitiatives still set to true.

This is because click event bubbles up the DOM tree combined with async nature of setState
First li.onClick fires calling selector which calls setState({ showInitiatives: false})
Then a.onClick fires calling setState({ showInitiatives: true }). You could check it is true say by adding log statement to it.
Now you have 2 pending updates
{ showInitiatives: false}
{ showInitiatives: true}
which when merged is noop.
You need to either stop event propagation inside li.onClick by calling e.stopPropagation() or rethink what a.onClick handler is doing.

Related

handleChange event listener not working for checkbox component

I'm trying to implement a simple checkbox that changes its checked state (true/false) upon clicking. But it does nothing when I click it. For example, upon clicking the checkbox and console logging state.box1 after the click, it's still set to true instead of false. I got the code from Material UI's checkbox example, so I'm confused as to why it doesn't work for me. Any ideas?
state = {
box1: true,
};
handleChange = name => event => {
setState({ name: event.target.checked });
};
<div>
<Checkbox
value="box1"
checked={state.box1}
onChange={this.handleChange('box1')}
label="Primary"
/>
</div>;
Assuming Checkbox component passes the event object to the onChange callback.
Your original handleChange return a callback function but it does not bind to the component context. So your event object will most likely be undefined when you click the checkbox.
you can modify the code to something like this.
// takes both the event object and
handleChange = (event, name) => {
setState([name]: event.target.checked);
};
<Checkbox
value="box1"
checked={state.box1}
onChange={(event) => handleChange(event, 'box1')}
label="Primary"
/>

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.

Call a React Component with an onClick event

I need to call a Component (ExampleComp), and when the button is clicked, call againthe component (ExampleComp). The idea is to call the Component(ExampleComp) as many times as you press the button.
function newComponent{
<ExampleComp/>
}
------
return(
<div>
<ExampleComp/>
<Button className="btnNew" onClick=
{newComponent}> Create a new Component</Button>
</div>
)
Actually i don't know how to do it exactly and i would apreciate your help.
You can use the state for this purpose. Let's say your state is something like this:
this.state = { items: [] };
You can render all the items like the following example:
return (
<div>
{this.state.items.map(item => {
return <ExampleComp exampleProp={item.exampleProp} />;
})}
<Button className="btnNew" onClick={newComponent}>
Create a new Component
</Button>
</div>
);
And finally, you can push an item into the state, and React will take care of the rest.
function newComponent{
newItem = { exampleProp: 'Something?' };
this.setState((state, props) => ({ items: [...items, newItem] }));
}
This will do the job. I just used "exampleProp" to be an example but you don't have to use it. Actually, the state can be just a number too. The important part is using state in every user interface change.
render(){
return (
<Button className="btnNew" onClick={ this.setState({ clicked:true }) }>Create a new Component</Button>
{
this.state.clicked ? {newComponent} : null
}
)
}
This would help but though not recommended by me as setState will re-render(load) the component again onClick.

setState being called too late

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>)
});

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