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.
Related
I'm trying to make a component with React and I have 2 classes ( the App class that has the state and the ImageType class that has a dropdown using select and option).
I want to change the state in the App when I make a selection in the ImageType class but I get the error (out.js:6 Uncaught TypeError: Cannot read property 'value' of undefined).
I know that I'm doing something wrong but I can't realize what.
I want the "value" of the option tag to became the new value of this.state.field
Thank you
class ImageType extends React.Component{
onDone=()=>{
if(typeof this.props.done === "function"){
this.props.done(this.props.imageType)
}
}
render(){
return(
<div>
<select value={this.props.imageType} onChange={this.onDone}>
<option value="illustration">Illustration</option>
<option value="vector">Vector</option>
</select>
</div>
)
}
}
class App extends React.Component{
state={
field:""
}
done = (event) =>{
this.setState({
field: event.target.value
})
}
render(){
return(
<div>
<ImageType imageType={this.state.field} done={this.done}/>
</div>
)
}
}
In ImageComponent you have to pass event in onDone function:
// replaced brackets with event
onDone = event => {
if(typeof this.props.done === "function"){
this.props.done(event)
}
}
#Spartacus are right, since this.props.done function accepts a event parameter, you shouldn't pass a this.props.imageType which is not event type.
Or you can just pass the selected image type to the callback in the App component.
ImageType component
onDone = event => {
if (typeof this.props.done === "function") {
this.props.done(event.target.value);
}};
pass the selected value to the callback function in App component,
App component
done = imageType => {
this.setState({
field: imageType
});};
check the demo here
I have an app with one child component that I would like to re-render when setState updates the bookInput in the parent's state. I am using axios to request info from google's book api. For some reason, even though the state is updating, the child is not re-rendering. Please help if you can! Thank you!
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = {
bookInput: 'ender',
bookSubmitted: 'initial'
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSubmitEmpty = this.handleSubmitEmpty.bind(this);
}
handleChange(e) {
this.setState({bookInput: e.target.value});
console.log(this.state.bookInput);
//this.setState({bookSubmitted: false});
}
handleSubmit(e) {
e.preventDefault();
//this.setState({bookSubmitted: true})
const name = this.state.bookInput;
this.setState({bookInput: name});
console.log(this.state);
this.setState({bookSubmitted: 'userSub'});
}
handleSubmitEmpty(e) {
alert('please enter an item to search for');
e.preventDefault();
}
render() {
return (
<div className="App">
<header className = "App-header">
<h1>Book Search App</h1>
</header>
<form className = "form-style" onSubmit = {this.state.bookInput ? this.handleSubmit: this.handleSubmitEmpty}>
<label>
<input type="text" className = "input-style"
value = {this.state.bookInput} onChange = {this.handleChange}>
</input>
</label>
<button type="submit">search books</button>
</form>
{/* <Book bookInput = {this.state.bookInput}/> */}
{/*this.state.bookSubmitted && <Book bookInput = {this.state.bookInput}/>*/}
{
(this.state.bookSubmitted === 'initial' || this.state.bookSubmitted === 'userSub') &&
<Book bookInput = {this.state.bookInput}/>
}
</div>
);
}
}
export default App;
class Book extends Component {
constructor(props) {
super(props);
this.state = {
//bookInput2: "ender",
bookTitles: [],
bookExample: '',
isLoading: false
}
this.bookClick = this.bookClick.bind(this);
}
bookClick(book) {
console.log(book);
console.log(book.volumeInfo.infoLink);
const bookURL = book.volumeInfo.infoLink;
window.open(bookURL);
}
componentDidMount() {
//this.setState({ isLoading: true });
this.setState({isLoading: true});
axios.get(`https://www.googleapis.com/books/v1/volumes?q=${this.props.bookInput}`)
.then((response) => {
const bookExample1 = response.data.items;
console.log(bookExample1);
this.setState({bookTitles: bookExample1, isLoading: false});
})
.catch((error) => {
console.error('ERROR!', error);
this.setState({isLoading: false});
});
}
render() {
return (
<div>
{ this.state.bookTitles ? (
<div>
<h2>book list</h2>
{<ul className = 'list-style'>
{this.state.isLoading &&
(<div>
loading book list
</div>)
}
{this.state.bookTitles.map(book => (
<li key={book.id}>
<span className = 'book-details book-title' onClick = {() => this.bookClick(book)}> {book.volumeInfo.title}</span>
<br/>
{book.volumeInfo.imageLinks &&
<img src = {book.volumeInfo.imageLinks.thumbnail}/>
}
{ book.volumeInfo.description &&
<span className = 'book-details'>{book.volumeInfo.description}</span>
}
<br/>
<span className = 'book-details'>Categories {book.volumeInfo.categories}</span>
</li>
))}
</ul>}
</div>) :
(<p>sorry, that search did not return anything</p>)}
</div>
);
}
}
May be you are looking for something similar to this?
https://stackblitz.com/edit/react-snoqkt?file=index.js
The above code can be simplified more and organized but it gives you some idea.
Main changes in the code.
Changed Api call from componentDidMount lifecycle event to a new method named getInitialdata which is called in handleSubmit.
getInitialdata(name){
axios.get(`https://www.googleapis.com/books/v1/volumes?q=${name}`)
.then((response) => {
const bookExample1 = response.data.items;
console.log(bookExample1);
this.setState({bookTitles: bookExample1, isLoading: false, bookSubmitted: 'userSub'});
})
.catch((error) => {
console.error('ERROR!', error);
this.setState({isLoading: false, bookSubmitted: 'userSub'});
});
}
Changed the way how Child component is used.
<Book bookTitles={this.state.bookTitles} isLoading={this.state.isLoading}/>
Issue with your code is you are making an API call in your component's didMount method. This lifecycle event will be invoked only when the component is mounted. Not when it is updated.
When you enter some input in your textbox and click on "Search books", componentDidMount event doesnt fire. And this is the reason why API calls are not happening from the second time.
More on the lifecycle events at https://reactjs.org/docs/react-component.html#componentdidmount
I've taken your code and extrapolated it into this sandbox. Just as you said, your parent component state is updating as it should, but the problem is that the child component doesn't change its state.
A state change will always trigger a re-render in React. The only problem is, your child component is managing it's own state, which isn't directly changing. Instead, it's just receiving new props again and again, but not doing anything with them.
If you look at your code for the <Book /> component, you only modify its state on componentDidMount, which only happens once. If you'd like to programmatically make it update, you can do one of two things.
Remove state from the child component, and make it rely entirely on props, so that it stays in sync with the parent
Use the componentDidUpdate lifecycle method (docs) to choose when to change the state of the child (which will trigger the re-render)
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();
}
I'm trying to use the select tag in React. I have the following component:
class InteractionHeader extends React.Component {
constructor(props) {
super(props);
this.state = {
allDays: [],
selected: ""
};
this.dateChanged = this.dateChanged.bind(this);
}
componentDidMount() {
this.setState({
allDays: ["a", "b"],
selected: "a"
});
}
dateChanged(e) {
e.preventDefault();
console.log(event.target.value);
}
}
And in my render, I have the following:
render() {
return (
<div>
<select value={this.state.selected} onChange={this.dateChanged}>
{this.state.allDays.map((x) => {
return <option key={`${x}`} value={`${x}`}>{x}</option>
})};
</select>
</div>
);
}
However, when I select a different option, my console prints undefined instead of the option I selected. What is going on and how do I prevent it?
You are calling it wrong
dateChanged(e) {
console.log(e.target.value);
}
You should use prevent default in case where you donot want your page to take a refresh and try to make a server side call. In case of select there is nothing like this.
You have a typo in dateChanged(e) method. You need to log e.target.value instead of event.target.value.
I am newbie in React and Typescript.In AlertDismissable class I am setting property of show when a request completed.I have used this sample and changed it a bit.
Depending on response I change AlertDismissable's contents and style.
When a user clicks hide button I am trying to set its show property to false.I have bound states with properties that's why I am trying to set props.However,compiler throws
TS2540: Cannot assign to 'show' because it is a constant or a read-only property.(handleDismiss method)
It seems generic props are read-only by default.Is there any other way to make it work ?
Here is my AlertDismissable tsx
import * as React from 'react'
import { Alert, Button } from 'react-bootstrap';
interface AlertDismissableState {
show: boolean;
style: string;
}
export default class AlertDismissable extends React.Component<AlertDismissableState, AlertDismissableState> {
constructor(props: any, context: any) {
super(props, context);
this.handleDismiss = this.handleDismiss.bind(this);
this.handleShow = this.handleShow.bind(this);
this.state = {
show: this.props.show,
style: this.props.style
};
}
handleDismiss() {
this.props.show=false;
}
handleShow() {
this.props.show=true;
}
render() {
if (this.props.show && this.props.style == "Success") {
return (
<Alert bsStyle="success">
<p>
Ok
</p>
<Button onClick={this.handleDismiss}>Hide Alert</Button>
</Alert>
);
}
if (this.props.show && this.props.style == "Danger") {
return (
<Alert bsStyle="danger">
<p>
Failed
</p>
<Button onClick={this.handleDismiss}>Hide Alert</Button>
</Alert>
);
}
return (<div />);
}
}
Here is my component that includes AlertDismissable class.I removed some codes for brevitiy.
export default class UploadContainer extends React.Component<{}, UploadContainerState> {
uploadFile(event: any) {
fetch("ProposalData/UploadFile", {
...
})
.then(handleErrors)
.then(function (response) {
that.setState({ alertVisible: true, style: "Success" });
}).catch(function (error) {
that.setState({ alertVisible: true, style: "Danger" }); });
}
render() {
return (
<div>
<AlertDismissable show={this.state.alertVisible} style={this.state.style} />
</div>)}
typescript:2.9.1
react: "^16.4.0"
react-bootstrap: "^0.32.1"
react-dom: "^16.4.0",
You are asking the wrong question. I will be answering a different one but the answer is the following:
In React you should never try to change props. Properties are what is being passed from the parent component. If you want to change properties, you have to go to the parent component and change what is being passed to AlertDismissable.
Actually, you should pass a property onDismiss of type function to AlertDismissable and call this.props.onDismiss() instead of this.props.show = false. Then you need to change the state of UploadContainer in that function.
Also note that your AlertDismissable class does not need to maintain state at all and it should use props directly.
I am not a typescript developer myself but it should be something like this:
interface AlertDismissableState {
show: boolean;
style: string;
onDismiss: () => void;
}
and then just:
<Button onClick={this.props.onDismiss}>
Hide Alert
</Button>
and in the parent component:
<AlertDismissable
show={this.state.alertVisible}
style={this.state.style}
onDismiss={() => this.setState({ alertVisible: false })}
/>
By the way, it does not make much sense to render AlertDismissable component when it should be hidden. You could consider:
{this.state.alertVisible && (
<AlertDismissable
style={this.state.style}
onDismiss={() => this.setState({ alertVisible: false })}
/>
)}
instead of passing the flag and render an empty <div/>.