Programmatically cause onBlur to trigger in react - javascript

I use onBlur to close a dropdown, but I also want to handle a click handler of an li which is render within, setState won't work here, the behavior is broken when user try to open the dropdown again, try it here:
http://jsfiddle.net/ur1rbcrz
My code:
toggleDropdown = () => {
this.setState({
openDropdown: !this.state.openDropdown
})
}
render() {
return (
<div>
<div tabIndex="0" onFocus={this.toggleDropdown} onBlur={this.toggleDropdown}>
MyList
<ul className={this.state.openDropdown ? 'show' : 'hide'}>
<li>abc</li>
<li>123</li>
<li onClick={()=> this.setState({openDropdown:false})}>xyz</li> {/* not working */}
</ul>
</div>
</div>
);
}

Your code is not working because, even though you click li, a div container with onBlur event still is focused.
We add to your list container ref, after that we can call .blur(). We use it in your onClick li event handler.
this.dropDownList.blur()
See working example jsfiddle.
Or run this snippet:
class Hello extends React.Component {
constructor() {
super()
this.state = {
isDropdownVisible: false
}
this.toggleDropdown = this.toggleDropdown.bind(this);
}
toggleDropdown() {
this.setState({
isDropdownVisible: !this.state.isDropdownVisible
})
}
render() {
return (
<div>
<div
tabIndex="0"
ref={c => this.dropDownList = c}
onFocus={this.toggleDropdown}
onBlur={this.toggleDropdown}>
MyList
<ul
className={this.state.isDropdownVisible ? 'show' : 'hide'}>
<li>abc</li>
<li>123</li>
<li onClick={() => this.dropDownList.blur()}>xyz</li> {/* not working */}
</ul>
</div>
</div>
);
}
}
ReactDOM.render(
<Hello initialName="World"/>,
document.getElementById('container')
);
.hide {
display: none
}
.show {
display: block !important;
}
div:focus {
border: 1px solid #000;
}
div:focus {
outline: none;
}
<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 id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

i added onClick event to your div and it worked, your code becomes:
render() {
return (
<div>
<div tabIndex="0" onClick={() => this.setState({openDropdown: !this.state.openDropdown})} onFocus={this.toggleDropdown} onBlur={this.toggleDropdown}>
MyList
<ul className={this.state.openDropdown ? 'show' : 'hide'}>
<li>abc</li>
<li>123</li>
<li onClick={()=> this.setState({openDropdown:false})}>xyz</li> {/* not working */}
</ul>
</div>
</div>
);
}

OnBlur is a React Synthetic event and can be used in two ways:
To trigger something:
const {useState} = React;
const Example = ({title}) => {
const [field, setField] = useState("");
return (
<div>
<p>{title}</p>
<p>Uppercase on blur</p>
<input type="text"
value={field}
onChange={e=>setField(e.target.value)}
//LOOK HERE !
onBlur={e=>setField(e.target.value.toUpperCase())}
/>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="OnBlur triggering:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Be triggered by something
const {
useState,
} = React;
const Example = ({
title
}) => {
const [field, setField] = useState("");
return ( <
div >
<
p > {
title
} < /p> <
p > Remove focus by pressing enter < /p> <
input type = "text"
value = {
field
}
onChange = {
e => setField(e.target.value)
}
//LOOK HERE !
onBlur = {
e => setField(e.target.value.toUpperCase())
}
onKeyPress = {
e => (e.key === 'Enter' ? setField(e.target.value.toLowerCase()) || e.target.blur() : null)
}
/> < /
div >
);
};
// Render it
ReactDOM.render( <
Example title = "OnBlur triggered:" / > ,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
So to programmatically cause onBlur to trigger in react is necessary add an event to watch your change.
More info:
React SyntheticEvents

Related

Change the state of arrows in a dropdown list

The code below illustrates a normal drop down list. To indicate a drop down list, I use a down arrow with
arrow_drop_down
This arrow remains static for me in any state of the list (open or closed). However, I would like that when clicking on the list, the arrow changes to
arrow_drop_up
.
Those. so that with two different states of the list, there would be two different arrows.
export default function FilterStatusCode() {
const [values, setValues] = React.useState([]);
const [isExpanded, setIsExpanded] = useState(false);
const toggleExpand = () => {
setIsExpanded(!isExpanded);
};
return <>
<div className="item-toggle-statuscode" onClick={toggleExpand}>
<h6>Status Code</h6>
<span class="material-icons">
arrow_drop_down
</span>
</div>
{ isExpanded &&
<div>
<TagInput
inputProps={{ placeholder: 'Add status code...' }}
values={values}
onChange={(values) => {
setValues(values)}}>
</TagInput>
</div>
}
</>;
}
try
<div className="item-toggle-statuscode" onClick={toggleExpand}>
<h6>Status Code</h6>
<span class="material-icons">
{ isExpanded ? arrow_drop_up : arrow_drop_down }
</span>
</div>
You can choose which arrow you use depending on the current state:
// If the list is open show the `up` arrow
// otherwise show the `down` arrow
<span className={open ? "up" : "down"}></span>
I had to improvise in this example and used unicode in the class names.
const { useState } = React;
function Example() {
return (
<div>
<Item />
<Item />
</div>
);
}
function Item() {
const [ input, setInput ] = useState('');
const [ open, setOpen ] = useState(false);
function handleChange(e) {
setInput(e.target.value);
}
function handleOpen() {
setOpen(!open);
}
function handleClick() {
console.log(input);
}
return (
<div className="item">
<div onClick={handleOpen} className="heading">
<span>Status code</span>
<span className={open ? "up" : "down"}></span>
</div>
{open && (
<div>
<input
type="text"
onChange={handleChange}
value={input}
/>
<button
type="button"
onClick={handleClick}
>Submit
</button>
</div>
)}
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.down:after { content: '\25BC'; }
.up:after { content: '\25B2'; }
.heading:hover { cursor: pointer; color: red; }
.item { margin-bottom: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation
Conditional (ternary) operator

using button to increment divs in react

I am fairly new to React/Next and I had a quick question.
I am trying to create a button that will increment the number of divs in real time.
Here is my code:
import React from 'react'
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function addClown(event) {
event.preventDefault();
}
return(
<React.Fragment>
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={activityId} id={activityId} />
<br />
</div>
)
},
)}
<span className="clown-add">
<button onClick={addClown} onChange={() => { setClownCounter(clownCounter++) }}>Add Clown</button>
</span>
<br />
</form>
</div>
</React.Fragment>
)
}
export default Clown
As you can see the goal is to increase the amount of clown-box divs everytime the button is clicked. I think I am close but it is not currently working. Can anyone help?
There are few small this wrong with your code.
First, you have an extra comma(,) after the return statement in map function
Second, you are updating state clownCounter on onChange event in button, which is incorrect. You should update it on click and also prevent the default behaviour of form submit on click of button or you can define the button type to be type="button"
Lastly, you need to define your onChangeForm function
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function onChangeForm() {
}
function addClown(event) {
event.preventDefault();
setClownCounter(prev=> prev+1);
}
console.log(clownCounter);
return(
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`;
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={'activityId'} id={'activityId'} />
<br />
</div>
)
})
}
<span className="clown-add">
<button type="button" onClick={addClown}>Add Clown</button>
</span>
<br />
</form>
</div>
)
}
ReactDOM.render(<Clown />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />
Edit: Thought issue was caused by Array.from, but, it's not. I've removed that part, but kept the example since OP might find it useful
const { useState } = React;
const Clowns = ({ title }) => {
const [clownCounter, setClownCounter] = React.useState(1);
return (
<div>
<button onClick={() => setClownCounter(clownCounter + 1)}>
Add clown
</button>
<div className='clowns'>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map((e, i) => (
<div>
<h4>{`Clown #${i + 1}`}</h4>
<img src={`https://placehold.it/150x150&text=Clown%20%23${i + 1}`} />
</div>
))}
</div>
</div>
);
};
ReactDOM.render(<Clowns />, document.getElementById("react") );
.clowns { display: flex; flex-direction: column; }
h4 { margin-bottom: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to have the children affect the parent in React?

I have a situation where I have something like this:
<DropDown>
<p>Select an option:</p>
<button onClick={() => console.log("opt 1")}>Option 1</button>
<button onClick={() => console.log("opt 1")}>Option 2</button>
</DropDown>
The DropDown component is one that I wrote, that renders this.props.children in a drop-down fashion. The DropDown has an onClick call that makes it close.
DropDown looks something like this (simplified):
class DropDown extends Component {
state = {
open: false
};
render() {
return (
<div className={`drop-down ${this.state.open ? "open" : "closed"}`}>
<div className="closed-version">
<div className="header" onClick={this.open}>
<div className="header-contents">Click here to select an option</div>
</div>
</div>
<div className="open-version">
<div className="content" onClick={this.closeWithoutSelection}>
{this.props.children}
</div>
</div>
</div>
);
}
open = () => {
this.setState({ open: true }, () => {
if (this.props.onOpen) {
this.props.onOpen();
}
});
};
closeWithoutSelection = () => {
this.setState({ open: false }, () => {
if (this.props.onCloseWithoutSelection) {
this.props.onCloseWithoutSelection();
}
});
};
}
The issue I'm running into is that I want to do something different to the DropDown whether it was closed selecting an option or not. How do I go about doing that?
In your DropDown component you have some state and you are probably rendering your children like this:
this.props.children
Instead you can use render props to pass state, methods or anything else down to your children without having to handle it outside of the DropDown component at the parent level.
class DropDown extends Component {
// constructor / state, methods...
yourSpecialMethod(item) {
// do your special thing here
}
render() {
// pass state or methods down to children!
return this.props.children(yourSpecialMethod)
}
}
Then modify your render slightly:
<DropDown>
{handleSpecialMethod =>
<>
<p>Select an option:</p>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 1</button>
<button onClick={() => handleSpecialMethod("opt 1")}>Option 2</button>
</>
}
</DropDown>
UPDATED
Accordingly #pupeno (and he is right), the dropdown logic should be within the dropdown itself. However, we should pass a callback function in order to deal with the chosen data.
class DropDown extends React.Component {
state = {
open: false,
};
toggleDropdown = (e) => {
this.setState({
open: !this.state.open,
});
const value = e.target.getAttribute('value');
if ( value !== "null") {
this.props.selectItem(value);
}
};
render() {
const { selectItem } = this.props;
const { open } = this.state;
return (
<div className={open ? "drop-down open" : "drop-down"}>
<div onClick={this.toggleDropdown} value="null">Select</div>
<div onClick={this.toggleDropdown} value="1">Item 1</div>
<div onClick={this.toggleDropdown} value="2">Item 2</div>
<div onClick={this.toggleDropdown} value="3">Item 3</div>
</div>
);
}
};
class App extends React.Component {
requestItem = (item) => {
alert(`Request item ${item}`);
};
render() {
return (
<div>
<DropDown selectItem={this.requestItem}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);
.drop-down {
border: 1px solid black;
width: 180px;
height: 30px;
overflow: hidden;
transition: height 0.3s ease;
}
.open {
height: 120px;
}
.drop-down > div {
padding: 5px;
box-sizing: border-box;
height: 30px;
border-bottom: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
You could use an onChange function and call the parent function that is passed down as a prop. This will "affect the parent" as you asked.
this.props.onChange() or something similar.

How to totally disable a react component?

I have a react component which has some buttons and text inputs. I want to totally disable all the things inside this component until some other works are complete. How to make the entire inner component disabled?
You can add disabled props and use CSS to disable div
const MyComponent = ({disabled}) => {
return (
<div style={disabled ? {pointerEvents: "none", opacity: "0.4"} : {}}>
<h1> Text</h1>
<input type="text"/>
<input type="password"/>
<button>Login</button>
</div>
)
}
Better to use form and fieldset, and put all the input/button elements inside that. You can disable all of them by setting the property disabled to fieldset.
Refer MDN Doc for more details.
Working example:
class App extends React.Component {
constructor () {
super()
this.state = { disable: false }
}
toggleDisable = () => this.setState(prevState => ({disable: !prevState.disable}))
buttonClick = (e) => {
e.preventDefault();
console.log('button clicked');
}
render (){
return (
<div>
<button className='toggle' onClick={this.toggleDisable}>Toggle Disable</button>
<form>
<fieldset disabled={this.state.disable}>
<input />
<button onClick={this.buttonClick}>Button</button>
</fieldset>
</form>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
.toggle {
margin-bottom: 20px;
}
<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 id='app' />
All you need to do is add a disabled prop on the component and pass it on to the inner fields like
<MyComponent disabled={shouldComponentBeDisabled} {...otherProps} />
and in implementation
const MyComponent = ({disabled}) => {
return <div>
<button disabled={disabled}>someBtn</button>
<input type="text" disabled={disabled}/>
</div>
}
You can use disabled prop pattern to save the day.
const App = () => {
return (
<div>
<SomeComponent disabled />
</div>
);
};
const SomeComponent = ({ disabled = false }) => {
return (
!disabled && (
<div>
<h2>Disable SomeComponent to see magic happen!</h2>
</div>
)
);
};

React bind function to each item inside array

I'm trying to make it so when you click on the dropdown arrow the settings dropdown will appear.
When I currently press an arrow dropdown, all the settings dropdown open that are within the array loop.
This is the function that renders the loop:
viewPublishedPages() {
const pages = this.state.pages;
return (
<div>
{pages.map((val, i) => {
let dropdown = 'none';
return (
<div className="block" key={i}>
<div className="columns">
<div className="column is-10">
<p>PUBLISHED</p>
<h2>{val.title}</h2>
</div>
<div className="column">
<div className="settings">
<div className="arrow__container">
<div className="arrow" onClick={this.showSettings.bind(this, i)} />
</div>
{
this.state.settingPanel
?
<ClickOutside onClickOutside={::this.hide}>
<div className="arrow__dropdown">
<Link href={{pathname: '/admin/edit-page', query: {title: val.title}}}>
<a className="arrow__dropdown__link">Edit</a>
</Link>
<button
className="arrow__dropdown__delete"
onClick={() => this.handleDelete(i)}>Delete</button>
</div>
</ClickOutside>
: null
}
</div>
</div>
</div>
</div>
);
})}
</div>
);
}
Notice: <div className="arrow" onClick={this.showSettings.bind(this, i)} />
This is the state:
static dataStruc () {
return {
loading: true,
settingPanel: false,
pages: [],
};
}
Your are currently saving a boolean value to settingPanel and therefore all dropdowns open upon click.
My suggestion is replace settingPanel from boolean to the respective page id. In case you don't have page ids, then store the current page index on it.
That makes it easier to render the dropdown so you have access/control to the selected one and later render its settings:
showSettings(index) {
this.setState({
settingPanel: index,
})
}
And then in viewPublishedPages:
{this.state.settingPanel === i &&
<ClickOutside onClickOutside={::this.hide}>
..
</ClickOutside>}
I wrote a sample code so you get the idea.
class App extends React.Component {
constructor() {
super()
this.state = {
pages: [
{ title: 'Home' },
{ title: 'Contact' },
{ title: 'Page' }
],
settingPanel: -1,
}
this.showSettings = this.showSettings.bind(this)
}
showSettings(index) {
this.setState({
settingPanel: this.state.settingPanel === index ? -1 : index,
})
}
render() {
const { pages, settingPanel } = this.state
return (
<div>
{pages.map((page, index) =>
<div key={index} className="page">
<div onClick={this.showSettings.bind(this, index)}>
{page.title}
</div>
{settingPanel === index &&
<div className="settings">
<div>Setting 1</div>
<div>Setting 2</div>
<div>Setting 3</div>
</div>
}
</div>
)}
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
.page {
background-color: cyan;
margin-top: 10px;
padding: 10px;
}
.settings {
background-color: black;
color: white;
}
<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 id="root"></div>

Categories

Resources