Trigger a click event on element and then unmount it - reactjs

I'm trying to implement a suggestion list. I use <input/> followed by a number of <div>s. When the input element is focused, list, or divs, will show; when the input element is not focused, or blurred, the divs are unmounted. I want to print the content in div before the boolean variable isFocused is set to false. But now if I blur the input and click div, the printContent will not be triggered because isFocused has been set to false. Now I am using setTimeout to deal with this problem. Is there a better way to resolve this? Here is the code snippet:
CodeSandbox
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFocused: false
}
}
handleFocus = () => {
this.setState({ isFocused: true });
}
handleBlur = () => {
setTimeout(() => this.setState({ isFocused: false }), 1000);
}
printContent = () => {
console.log('print content');
}
render() {
console.log(this.state.isFocused)
return(
<div>
<input onFocus={this.handleFocus} onBlur={this.handleBlur} />
{
this.state.isFocused ?
<div key='1' onClick={this.printContent}>content1</div> :
null
}
</div>
);
}
}

Related

How to update component state that is connected by componentDidUpdate()

I am passing selectedOrderState as props from parent and want to populate the state and that works but can't figure how to change the state for use in an input field with an onChange=(handleChange) function attached to manipulate the data. Seems as though componentDidUpdate() and getDerivedStateFromProps() both seem to lock the state so no change can occur. **componentDidMount also does not work because the selectedOrderState prop comes from an onClick event and so the component had already mounted.
Code below - Any thoughts would be helpful!
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.setState({
Order:{
...this.state.Order,
[e.target.id]: e.target.value,
}
})
};
handleSubmit = () => {
};
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} = this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text" onChange=
{this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder
Declare your state inside the constractor and bind your functions. I'm inviting you to take a look to forms docs with react
constructor(props) {
super(props);
this.state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
Probably the most hackerish way to do something but it worked:)
What i did was keep the componentDidUpdate() feeding the state to the child component but from the parent i passed down a function called handleChangeUP() for which i was able to use pass the event of onChange data through to change the original state selectedOrderState. Have a look!
Child
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.props.handleChangeUP(e)
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} =
this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">
{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text"
value={this.state.Order.DeliveryDate}
onChange={this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder
Parent
import React, { Component } from 'react'
import Child from './child'
export class Parent extends Component {
state = {
creating: false,//creat order window toggle
profiling: false,//Profile window toggle
chatting: false,//Chat window toggle
searching: false,//Search inside Chat window
selectedOrder: {}
};
handleChangeUP = (e) => {
console.log(e.target.id);
this.setState({
// [e.target.id]: e.target.value
//Order: e.target.value
selectedOrder:{
...this.state.selectedOrder,
[e.target.id]: e.target.value
}
})
}
render() {
return (
<div className="Wrapper">
<Child handleChangeUP={this.handleChangeUP}
selectedOrderState={this.state.selectedOrder}/>
</div>
)
}
}
export default Parent;

React-Select how to keep default behavior of menu open/close plus add to open menu on button click?

I have a react-select component to preform search on an api , and a button to start the search.
i've tried to set menuIsOpen={true} after button click but it ruins the original focus blur behavior the menu is no longer closing on blur.
tried to set .focus() on the input it didn't open the menu.
current original behavior menu is opening on focus and closing on blur , i want to keep this plus i want to open the menu after i click the button.
To achieve what you want you will need to use a controlled menuIsOpen props.
I think the easiest way to keep all the native functionality of react-select is to use a combination of the props onInputChange and onFocus like the following code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuIsOpen: false
};
}
onInputChange = (options, { action }) => {
if (action === "menu-close") {
this.setState({ menuIsOpen: false });
}
};
openMenu = () => {
this.refs.focus();
this.setState({ menuIsOpen: true });
};
render() {
return (
<div className="App">
<Select
ref={r => {
this.refs = r;
}}
options={options}
onFocus={this.openMenu}
onInputChange={this.onInputChange}
menuIsOpen={this.state.menuIsOpen}
/>
<button onClick={this.openMenu}>Open Select</button>
</div>
);
}
}
Here a live example.
If you want to preserve the focus out behaviour you should do something like this
add a ref at the constructor
constructor(props) {
super(props);
this.state = { open: false }
this.selectRef = React.createRef();
}
then add this close method
close() {
this.setState({ open: false });
this.selectRef.current.blur();
}
and on the select component
<Select
closeMenuOnSelect={props.closeMenuOnSelect || true}
openMenuOnClick={() => this.setState({ open: true })}
onFocus={() => this.setState({ open: true })}
onBlur={() => this.setState({ open: false })}
menuIsOpen={this.state.open}
onInputChange={() => {
if (props.closeMenuOnSelect) {
this.close();
}
}}
ref={this.selectRef}
>
</Select>
I am using this function in React, to keep open only one tab, so as you open another one, previous will be closed.
const openSingleTab = (id) => {
const newData1 = data.map((item) => {
if(item.id === id){
return {...item, isOpened: !item.isOpened}
}
return {...item, isOpened: false}
})
setData(newData1)
}
This option is good option without managing the state.
openMenuOnFocus={true}
This also work on tab button click because when click on tab button then dropdown have the focus.

Controlled Inputs, displayed values will not update to last digit

i'm using React(Typescript Version) to display some input inside a form.
The problem (as you can see from the image) is that when i update the values, from the setState function, values will not 'scroll' on the right
render() {
return(
<input
name={this.props.input.Name}
type={this.props.input.Type}
defaultValue={this.state.value}
ref={this._input}
key={key()}
)}
The function that updates the Value is a common set Function :
public set Value(data: string) {
this.setState({
internalValue: data,
inputError: !this.validateValue(data)
});
}
Note that the input works as expected if i write from the Keyboard, but if i write the input using a 'simulated' keyboard on screen happens what i just described
Any ideas?
Thank you
Update after simbathesailor support:
render() {
return(
<input
name={this.props.input.Name}
type={this.props.input.Type}
defaultValue={this.state.value}
ref={this._input}
key={key()}
onChange={this.setValue}
/>
)
}
componentDidUpdate(prevProps: InputProps, prevState: InputState) {
if (prevState.value!== this.state.value) {
this._input.current.focus();
}
}
setValue(event: React.ChangeEvent<HTMLInputElement>) {
console.log('change');
this.setState({
value: event.target.value
})
}
shouldComponentUpdate(nextProps: InputProps, nextState: InputState): boolean {
return (this.state.value!= nextState.value);
}
public set Value(data: string) {
this.setState({
value: data,
inputError: !this.validateValue(data)
}, () => {
this._input.current.focus();
});
}
You can use the refs and commit lifecycle method componentDidUpdate method. to achieve this.
In the example mentioned below, it is done for the uncontrolled component. But idea will remain same for controlled component also.
class Test extends React.Component {
constructor(props) {
super(props)
this.InputRef = React.createRef()
this.state = {
value: 0
}
}
setValue = (event) => {
this.setState({
value:event.target.value
})
}
update = () => {
this.setState({
value: (this.state.value || 0) + 1000
})
}
componentDidUpdate(prevProps, prevState) {
if(prevState.value !== this.state.value) {
this.InputRef.current.focus()
}
}
render() {
return (
<div>
<input
value={this.state.value}
onChange={this.setValue}
ref={this.InputRef}
/>
<button onClick={this.update}>update</button>
</div>
)
}
}
ReactDOM.render(<Test />, document.getElementById("root"))
Here is the codepen link to see it working:
Uncontrolled approach(javascript) codepen link
Controlled approach(javascript) codepen link
I have tried typescript for the first time. Thanks for your question :). Typescript is good. And here is your desired solution needed in typescript.
Codesandbox link(Typescript)

Only change text on a new click, instead of real time in React

I am trying to keep my output text only show when I the new search button is clicked.
Right now, I have a results bar that show the search term that a user is looking for, initially is hidden, but when a search is entered you see the results bar with the term.
the problem is as users enter a new search, the text on the result bar changes, and what I would like to do, is to only have the text change when they click the search button again.
seems simple in premise, but I am having a hard time figuring it out.
class App extends Component {
constructor(props){
super(props);
this.state = {
searchedTerm: '',
}
}
handleChange = event => {
this.setState({
...this.state,
searchedTerm: event.target.value,
})
}
<ProductList
list={list}
term={searchedTerm}
hideResultsBar={hideResultsBar} />
//different component
export default props => {
const { hideResultsBar, term, list } = props;
return(
<div>
{
hideResultsBar ? null : <SearchResultBar term={term} />
}
Instead of using an onChange event, you'll need to move your setState logic into a submit handler when you click the search icon or press enter in the search field. You'll also need to assign a ref to the search input so that you can grab the value from it:
handleSearchSubmit = () => {
this.setState({
searchedTerm: this.searchInput.value,
})
}
render() {
return (
<div>
<input type="text" ref={ ref => this.searchInput = ref } />
</div>
)
}
You could add another property to your state for the showing and hiding of the results and whenever the text is changed it hides the results, and clicking the button shows them, e.g:
class App extends Component {
constructor(props){
super(props);
this.state = {
searchedTerm: '',
hideResultsBar: true
}
}
handleChange = event => {
//you don't need to set all items with ...this.state
//like you would with redux
this.setState({
searchedTerm: event.target.value,
hideResultsBar: true
});
}
handleSearchClick = () => {
this.setState({ hideResultsBar: false });
}
}
<ProductList
list={list}
term={searchedTerm}
hideResultsBar={hideResultsBar}
/>
export default props => {
const { term, list, hideResultsBar } = props;
return(
<div>
{
hideResultsBar ? null : <SearchResultBar term={term} />
}
</div>
);
};

Create a custom radio button using React JS

I'm trying to create a custom radio button. The issue that i'm facing is that i'm unable to uncheck the radio button when another radio button is clicked. Currently it behaves like a checkbox.
import {React, ReactDOM} from '../../shared/lib/react';
export default class RadioButton extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedRadioBtn: false
};
this.toggleRadioBtn = this.toggleRadioBtn.bind(this);
};
toggleRadioBtn(){
this.setState({checkedRadioBtn: !this.state.checkedRadioBtn});
};
render() {
return (
<div className="radio-btn-group">
<div onClick={this.toggleRadioBtn} className={this.state.checkedRadioBtn ? "radiobtn checked" : "radiobtn unchecked"} data-value={this.props.value}></div>
<label>{this.props.text}</label>
</div>
);
}
};
You need to have container for group of radio buttons. That container will maintain the state for all the radio buttons and handle check/uncheck for each option. Here is the sample code for that,
import React from 'react'
import ReactDOM from 'react-dom'
class RadioBtn extends React.Component{
constructor(props) {
super(props);
}
handleClick(){
this.props.handler(this.props.index);
}
render() {
return (
<div className="radio-btn-group" onClick={this.handleClick.bind(this)}>
<div className={this.props.isChecked ? "radiobtn checked" : "radiobtn unchecked"} data-value={this.props.value}></div>
<label>{this.props.text}</label>
</div>
);
}
}
class RadioGrp extends React.Component{
constructor() {
super();
this.state = {
selectedIndex: null,
selectedValue: null,
options: ["option 0","option 1","option 2","option 3"]
};
}
toggleRadioBtn(index){
this.setState({
selectedIndex: index,
selectedValue: this.state.options[index],
options: this.state.options
});
}
render() {
const { options } = this.state;
const allOptions = options.map((option, i) => {
return <RadioBtn key={i} isChecked={(this.state.selectedIndex == i)} text={option} value={option} index={i} handler={this.toggleRadioBtn.bind(this)} />
});
return (
<div>{allOptions}</div>
);
}
}
var app = document.getElementById('app');
ReactDOM.render(<RadioGrp />, app);
Since you're using a div for a custom checkbox that doesn't behave like a normal checkbox you should be checking value against the selected value.
toggleRadioBtn(e){
this.setState({checkedRadioBtn: e.target.value});
};
Another question that I have is that you are assuming a single checkbox here so I have to assume you have a calling component that returns multiple instances. If that is the case then you need to pass your onClick down so you can pass the value back up to the parent. Then pass the selected value back down.
This is an example that I have in my application.
var languges = this.props.languages;
var languageToSelect = this.state.selectedLanguage;
var handleChange = this.handleChange;
var languageRows = Object.keys(languges).map(function(key) {
var language = languges[key];
return <LanguageBlock
key={ key }
language={ language }
languageCode={ key }
checked={ languageToSelect === key }
handleChange={ handleChange }
/>;
});
In my use case I have multiple languages and onChange I pass the selected language back up then on rerender the selected language will be updated so the radio options will reflect the change.
handleChange: function handleChange(event) {
this.setState({ selectedLanguage: event.target.value });
},
The handle change just sets state for the new value. The language block itself is just a simple component so no need to make it a class/component.
const LanguageBlock = ({ checked, language, languageCode, handleChange }) => {
return (
<div className="truncification">
<input type="radio" name="lang" id={ 'lang_' + languageCode }
value={ languageCode } checked={ checked } onChange={ (evt) => { handleChange(evt); } } />
<label htmlFor={ 'lang_' + languageCode }>{ language }</label>
</div>
);
};

Resources