how to 'inject' confirm event into react-select? - reactjs

I am using this project https://github.com/JedWatson/react-select for select component. I use it to render a multi select component. Below is a sample code:
import Async from 'react-select/lib/Async';
<Async
className="user-select"
classNamePrefix="user-select"
defaultValue={this.state.defaultValue}
defaultOptions
isClearable={false}
loadOptions={this.loadOptions}
isMulti
/>
below is a screenshot. It renders two items Purple and Red.
The item will be removed when I click the close button after each item. How can I add a prompt model to ask user confirm before deleting?

on clicking the item send that particular id to the event handler function and set that value and id in the state and also do setState for modal to true to show the model when item is clicked
You also make sure to set the showModal to false when user clicks yes or no in the modal so that it will work next time when you want to delete other item.
constructor(props){
super(props);
this.state = {
itemId: 0,
showModal: false,
itemValue: ""
}
}
handleItem = event => {
this.setState({
itemId: event.target.id,
showModal: true,
itemValue: event.target.value
});
}
resetModalFlag = () => {
this.setState({
showModal: false
})
}
render(){
const { showModal, itemId, itemValue } = this.state;
return(
<div>
<Select onChange={this.handleItem} />
{showModal && <Modal id={itemId} itemValue={itemValue} resetModalFlag={this.resetModalFlag} />}
</div>
)
}
In Modal component access itemId and itemValue using this.props and you can show text like are you sure you want delete this.props.itemValue With yes or no button. When either one of these buttons clicked you need to call resetModalFlag in yes and no button event handler functions like
handleYesButton= () =>{
this.props.resetModalFlag();
}
handleNoButton= () =>{
this.props.resetModalFlag();
}

Related

setState is delayed when using onChange to trigger function (react)

I'm new to coding and have just recently started using React.
I am selecting items from a drop-down menu using e.target.value. I am then setting the state of a variable with the value of the selected item.
However, the state value seems to be delayed i.e
I select item A and in the console logs I have selected item A but the state remains empty. Only once I select a new item e.g. item B, does the state update to reflect I selected item A.(screen shot below)
screen shot of console logs
I'm not entirely sure what I'm missing
Please see the code below:
class GetApi extends React.Component {
constructor(props) {
super(props);
// Set initial state
this.state = {
error: null,
isLoaded:false,
projects: [],
title: ' ',
description:'',
url:'',
id: '',
// selectedOptionId: []
};
// Binding this keyword
this.handleChange = this.handleChange.bind(this);
}
// handle onChange event of the dropdown
handleChange = (e) => {
console.log(`target value is ${e.target.value}`)
//update value of item id to be deleted
const toDelete = e.target.value
this.setState({ id: toDelete })
console.log(`This.state.id = ${this.state.id}`)
if (this.state.id === null){
console.log('Error - Selected Option is Empty')
} else {
console.log(`Selected ID is: ${this.state.id}`)
}
}
render(){
return (
<Container>
{/* Delete Item */}
<div className="delete">
<select
id='deleteSelect'
name='projects'
label= 'Projects'
multiple={false}
// assign onChange function
onChange={this.handleChange}
>
{/* set list options */}
{projects.map((item)=>(
<option key={item.id}
// let the value of the of dropdown be the id
value = {item.id}
>{item.id}:{item.title}
</option>
))}
</select>
<Button
onClick={(e) => this.delete(e)}>Delete</Button>
</div>
</Container>
)
}
}
export default GetApi;
Yes, this is how setState works, it triggers a new render with the updated value but it doesn't update the current one.
Also I strongly encourage you to look at function components and hooks, classes have been kind of obsolete in React for many years.

focus() doesn't set on input field after displaing (use refs)

Dumb React question: how to set focus on input after it displayed and why my code doesn't work? (Without display toggle it works.)
edit: What I expect: after click on a button, the input field appears (by removing .dnone class) and get a focus on it. (But it doesn't.)
My code:
import "./styles.css"; // with .dnone class with display:none
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
this.state = {
active: false
}
}
click = (e) => {
e.preventDefault();
this.setState({
active: true
});
this.input.current.focus();
}
render () {
return (
<div className="App">
<input type="text" ref={this.input} className={(this.state.active ? "" : "dnone")} />
<button type="button" onClick={(e) => this.click(e)}>click</button>
</div>
);
}
}
live demo: https://codesandbox.io/s/immutable-sea-9884z?file=/src/App.js:0-607
Thank you!
The issue is that React state updates are asynchronously processed, so in the click handler when you enqueue a state update you are immediately attempting to focus on the input, but since the active state hasn't updated yet you can't, the dnone classname hasn't been removed and input made visible yet.
Move the focus logic into the componentDidUpdate lifecycle method to "respond" to the active state updating.
componentDidUpdate(prevProps, prevState) {
if (prevState.active !== this.state.active && this.state.active) {
this.input.current.focus();
}
}
click = (e) => {
e.preventDefault();
this.setState({
active: true
});
}

Trouble updating radio button state value in parent component from child element

I'm currently working a a multipage checklist app to make a common checklist procedure more efficient.
my parent component called MainForm has all of the states for my app. In my first child element, I had to fill some text inputs. The states are updating and saving as planned. My second page (or other child element) was the portion where my checklist would begin. The issue is my app is rending, but the radiobutton value isn't being sent to my state. I'm also having an issue where I can select the 'yes' radio button and then the 'no' radio button, but I can't go from 'no' to 'yes'. radioGroup21 is the radio group that's giving me problem. All other states are working.
I'm getting an error in my console that says:
"Checkbox contains an input of type radio with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props.
I've tried removing the value tag and the defaultValue line in my Radio elements, but no luck. I've tried creating constructor(props) in my parent element but I still kept having issues."
So far I've tried removing the defaultValue in my radio button and after I tried removing the value line. Unfortunately I this did not help.
I also read about controlled and uncontrolled inputs. I've tried changing my parent components state to put them in a constructor(props) bracket. But no luck.
I also tried to not use the handleChange function and use the setState function with values of {radioButton21 === 'yes'} but that didn't work.
//Parent Component
Class MainForm extends Component {
state = {
step: 1,
projectNumber: '',
projectName: '',
numberOfSystems: '',
buildSheet: '',
controlPhilosophy: '',
projectLayoutDrawing: '',
projSoftwareValidation: '',
CppDrawing: '',
radioGroup21: '',
}
nextStep = () => {
const { step } = this.state
this.setState({
step : step + 1
})
}
prevStep = () => {
const { step } = this.state
this.setState({
step : step - 1
})
}
handleChange = input => event => {
this.setState({ [input] : event.target.value })
}
render(){
const {step} = this.state;
const { projectNumber, projectName, numberOfSystems, buildSheet , controlPhilosophy, projectLayoutDrawing, projSoftwareValidation, CppDrawing, radioGroup21 } = this.state;
const values = { projectNumber, projectName, numberOfSystems, buildSheet, controlPhilosophy, projectLayoutDrawing, projSoftwareValidation, CppDrawing, radioGroup21 };
switch(step) {
case 1:
return <ProjectInfo
nextStep={this.nextStep}
handleChange = {this.handleChange}
values={values}
/>
case 2:
return <PrelimInspection
nextStep={this.nextStep}
prevStep={this.prevStep}
handleChange = {this.handleChange}
values={values}
/>
export default MainForm;
-----------------------------------
//Child Component
import React, { Component } from 'react';
import { Form, Button, Radio } from 'semantic-ui-react';
import { throws } from 'assert';
class PrelimInspection extends Component{
saveAndContinue = (e) => {
e.preventDefault();
this.props.nextStep();
}
back = (e) => {
e.preventDefault();
this.props.prevStep();
}
render(){
const { values } = this.props
return(
<Form color='blue' >
<h1 className="ui centered">System Installation</h1>
<Form.Field inline>
<Form.Field>System Properly Supported</Form.Field>
<Radio
label = {'Yes'}
name = {'radio21'}
value = {'Yes'}
onChange={this.props.handleChange('radioGroup21')}
defaultValue={values.radioGroup21}
/>
<Radio
label = {'No'}
name = {'radio21'}
value = {'No'}
onChange={this.props.handleChange('radioGroup21')}
defaultValue={values.radioGroup21}
/>
</Form.Field>
<Button onClick={this.back}>Back</Button>
<Button onClick={this.saveAndContinue}>Save And Continue </Button>
</Form>
)
}
}
export default PrelimInspection
The app is rendering and the layout is correct. Unfortunately the state values aren't being sent to the parent state.
I checked the documentation https://react.semantic-ui.com/addons/radio/#types-radio-group and I have found few things you missed:
1.) Radio component asked the checked props (but you did not supply it).
2.) Which then requires you to pass the value, in your case it should come from the parent component:
<PrelimInspection
valueFromParent={this.state["radioGroup21"]}
nextStep={this.nextStep}
handleChange={this.handleChange}
values={values}
/>
so in your Child Component' render, take the value:
render() {
const { values, valueFromParent } = this.props;
...
3.) Radio's onChange value is passed as the second param (obj.value).
<Radio
label={'Yes'}
name={'radio21'}
value={"Yes"}
checked={valueFromParent === 'Yes'}
onChange={this.props.handleChange("radioGroup21")}
...
/>
So you can take the selected value like this:
// MainForm
handleChange = input => (event, obj) => { // obj is the second param
console.log("sendin here", input, obj.value);
this.setState({ [input]: obj.value });
};

React - Show Alert each time I click on an image

Each time I click on an image, the changeAlertVisibility get called.
It then changes the alertVisible property of the state to true.
Then inside of the render method, if alertVisible value is true, an Alert (dialog box) should get rendered, otherwise nothing.
import React, { Component } from "react";
import Alert from "./Alert";
class ListItem extends Component {
state = {
alertVisible: false
};
changeAlertVisibility = () => {
this.setState(prevState => ({
alertVisible: !prevState.alertVisible
}));
};
render() {
return (
<div className="card__body">
<img
src={this.props.photo.urls.small}
onClick={() => this.changeAlertVisibility()}
/>
{this.state.alertVisible ? (
<Alert
photoDesc={this.props.photo.description}
photoPath={this.props.photo.urls.small}
photoAlt={this.props.photo.id}
/>
) : (
<></>
)}
</div>
);
}
}
export default ListItem;
It works, but only by every second click.
The first time, I click an image, the value of alertVisible changes to true and the Alert pops up.
But the second time, the value of alertVisible changes to false and no Alert pops up.
The third time it works again and so on.
How can I change the code that the Alert pops up on each click?
The problem is that this code is toggling alertVisible to the inverse of its previous state:
changeAlertVisibility = () => {
this.setState(prevState => ({
alertVisible: !prevState.alertVisible
}));
};
So, initially this.state.alertVisible is false. After the first click, it will be set to !this.state.alertVisible // => true. On the second click, you are setting it back to !this.state.alertVisible // => false.
In order to achieve what you want, you need to ALWAYS set alertVisible to true like this:
changeAlertVisibility = () => {
this.setState({ alertVisible: true });
};
Now, you probably want to set it back to false when the user closes the alert. I can't tell you exactly how to achieve that since I can't see the definition of your Alert component. However, I would add a onClose callback to your Alert that gets called when the alert is closed and then set alertVisible to false there:
import React, { Component } from "react";
import Alert from "./Alert";
class ListItem extends Component {
state = {
alertVisible: false
};
changeAlertVisibility = (visible) => {
this.setState({ alertVisible: visible });
};
render() {
return (
<div className="card__body">
<img
src={this.props.photo.urls.small}
onClick={() => this.changeAlertVisibility(true)}
/>
{this.state.alertVisible ? (
<Alert
photoDesc={this.props.photo.description}
photoPath={this.props.photo.urls.small}
photoAlt={this.props.photo.id}
onClose={() => this.changeAlertVisibility(false)}
/>
) : (
null
)}
</div>
);
}
}
export default ListItem;

Is it possible to open React-Select component as it mounts?

Not just focusing, but to actually open the component and display options.
I know this isn't simple on a regular select component (see Is it possible to use JS to open an HTML select to show its option list? ), but perhaps it is still possible with React-Select.
In react-select you can control the opening of the menu with menuIsOpen props. to achieve your goal I would use a combination of menuIsOpen, onInputChange and onFocus like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuIsOpen: true
};
}
onInputChange = (options, { action }) => {
if (action === "menu-close") {
this.setState({ menuIsOpen: false });
}
};
onFocus = e => {
this.setState({ menuIsOpen: true });
};
render() {
return (
<div className="App">
<Select
options={options}
onFocus={this.onFocus}
onInputChange={this.onInputChange}
menuIsOpen={this.state.menuIsOpen}
/>
</div>
);
}
}
onInputChange can receive the following events:
"set-value",
"input-change",
"input-blur",
"menu-close"
Depending of what kind of behaviour you're expecting I would update this live example.

Resources