I'm learning React, I made a page with Form.
This Form should get data from back-end via axios.
I need help because whatever I do, array doesn't display in the select options.
Example of data:
[{"country": "Germany" "code": 112 }]
import React, { Component } from 'react';
import { Row, Col, Button, Form } from 'react-bootstrap';
import axios from 'axios';
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
country: []
};
}
componentDidMount() {
axios
.get('URL')
.then((response) => {
console.log(response);
this.setState({
country: response
});
})
.catch((error) => console.log(error.response));
}
handleChangeCountry = (event) => {
this.setState({ country: event.target.value });
};
inputCountryHandler = (event) => {
this.setState({
input: {
country: event.target.value
}
});
};
render() {
// const { country} = this.state;
return (
<Form className="calculator-table">
<Form.Group controlId="first-row" className="Focus-line">
<Form.Label>Country</Form.Label>
<Form.Control
as="select"
className="User-Input"
placeholder=""
value={this.state.country}
onChange={this.handleChangeCountry}
id="country"
option={this.country}
/>
</Form.Group>
);
}
}
export default Form;
I want the array data to be displayed in drop down select.
Thanks for any answer
You should first parse the JSON response from the API.
componentDidMount() {
axios
.get('URL')
.then((response) => {
console.log(response);
this.setState({
country: JSON.parse(response) //parse the response
});
})
.catch((error) => console.log(error.response));
}
As per the docs, Form.Control don't accept option as props.
You must iterate your country array to get the options.
<Form.Control
as = "select"
className = "User-Input"
placeholder = ""
value = { this.state.selectedCountry }
onChange = { this.handleChangeCountry }
id = "country" //Remove this id, otherwise you will get the warning
>
{
this.state.country && this.state.country.length > 0 && this.state.country.map(countryItem => <option key={countryItem.country}>{countryItem.country}</option>)
}
</Form.Control>
You should have a separate state variable to store the selected value,
constructor(props) {
super(props);
this.state = {
country: [],
selectedCountry: '' //add this to store selected country value
};
}
And your handleChangeCountry function should be,
handleChangeCountry = (event) => {
this.setState({ selectedCountry: event.target.value });
};
Note: axios return response in JSON format, but the actual data is in response.data, so you should set your state as,
this.setState({
country: response.data
});
When you specify id = 'country', you will get warning,
Warning: controlId is ignored on <FormControl> when id is specified.
You should remove the id = 'country'.
I believe the issue is that the Form.Control component as select expects options as children components. So you would need to map over the response array like so:
<Form.Control as="select">
{this.state.country.map(response => {
<option>{response.country}</option>
})}
</Form.Control>
According to docs you must use array of option
<Form.Control
as="select"
className="User-Input"
placeholder=""
value={this.state.country}
onChange={this.handleChangeCountry}
id="country">
{
this.state.country.map((c, i) => <option value={c.code} key={i}>{c.country}</option>)
}
</Form.Control>
Also, you have to use 2 state variables, example
this.state = {
selectedCountries: [], // from select control
country: [] // from backend
}
And populate select value from this.state.selectedCountries
Related
I'm using react-select. When I'm selecting a determinate value from select I've got the next issue
TypeError: Cannot read property 'value' of undefined
Also, values fetched from reducer todoList are not showed, I can't see them.
This is my code:
import Select from "react-select";
import "./styles.css";
import { searchTodos } from "../../actions/ServiceActions";
class SelectedTodo extends Component {
constructor(props) {
super(props);
this.state = {
selectedTodo: ""
};
this.handleChange = this.handleChange.bind(this);
}
bringTodos = () => {
//WHEN I'M EXECUTING THESE CODE LINES I CAN'T SEE TODOS VALUES
return this.props.todoList.map(todo => {
return (
<option key={todo._id} value={todo._id}>
{todo.name}
</option>
);
});
};
handleChange = e => {
this.setState({ selectedTodo: e.target.value });
};
componentDidMount() {
this.props.searchTodos();
}
render() {
const { loading } = this.props;
if (loading) {
return <span>...Loading</span>;
}
return (
<Select
className="form-container"
classNamePrefix="react-select"
placeholder="Please, insert a todo"
value={this.state.selectedTodo}
onChange={this.handleChange}
options={this.bringTodos()}
/>
);
}
}
function mapState(state) {
return {
todoList: state.todosDs.todoList
};
}
const actions = {
searchTodos
};
SelectedTodo = connect(
mapState,
actions
)(SelectedTodo);
export default SelectedTodo;
The expected behavior is dropdown shows todos and when I'm selecting a todo value I'm not getting error
TypeError: Cannot read property 'value' of undefined
The package react-select directly returns the json that has the value like
{label:"ABC", value:"abc"}
Change this
handleChange = (e) =>{
this.setState({selectedTodo: e.target.value});
}
To this
handleChange = (e) =>{
this.setState({selectedTodo: e});
}
I think as the documentation of react-select says
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
this.handleChange will directly return the selected option rather then event object.
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
and I think you don't need to bind the handleChange method as well.
I have a form with select element. The values for options for select element comes from an API. So, I have to dynamically create the options. But, I am unable to get the select element from DOM.
Following is the code that have tried. I tried to access select ID element with findDOMNode. None of this is getting the element.
What do I need to do to get the element selected?
componentDidMount() {
companyUserNames()
.then(result => {
const companyUsername = result;
console.log(result);
//output ==> [ { userName: "ABC",fullName: "ABC XYZ"}, {userName:
// "DEF",fullName: "DEF QRW"}]
companyUsername.forEach(role => {
console.log(role);
const roledynamic1 = document.getElementById("name1");
console.log(roledynamic3);
//output = null
const roledynamic2 = this.refs.name1
console.log(roledynamic3);
//output = undefiend
const roledynamic3 = ReactDOM.findDOMNode(this.refs.name1)
console.log(roledynamic3);
//output = null
const newchild1 = document.createElement("option");
newchild1.value = role.userName;
newchild1.text = role.fullName;
roledynamic3.add(newchild1);
});
})
.catch(error => {
console.log(error);
});
}
render(){
return(
<form>
//some input field
<div className='select'>
<select
name='userName'
id='name1'
ref="name1"
className='input common-input-style'
maxLength='255'
value={this.state.userName.value}
onChange={this.handleInputChange}
>
<option>Name</option>
</select>
</div>
//some input field
<form/>
)
}
findDOMNode accepts a component as an argument, not a ref.
Try using the ref directly it should hold the DOM node.
Note that findDOMNode is a deprecated API and should be avoided.
Also, as Amin Paks mentioned you should consider switching to the current style of using refs with createRef
Why do you need to access DOM node when you can easily work with state in react.
Below is the working code with codesandbox link:-
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
state = {
names: []
};
companyUserNames = () => {
return new Promise(resolve => {
return resolve([
{ userName: "ABC", fullName: "ABC XYZ" },
{ userName: "DEF", fullName: "DEF QRW" }
]);
});
};
componentDidMount() {
this.companyUserNames()
.then(result => {
this.setState({
names: result
});
})
.catch(error => {
console.log(error);
});
}
render() {
const { names } = this.state;
let namesList =
names.length > 0 &&
names.map((item, i) => {
return (
<option key={i} value={item.userName}>
{item.fullName}
</option>
);
});
return (
<form>
<div className="select">
<select className="input common-input-style" maxLength="255">
{namesList}
</select>
</div>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I'm not sure what I'm doing wrong, but I have an input field for entering a search term and trying to filter results based on the search term. The problem is that the first value being passed is an empty string and input is offset by 1 item for each keypress after that. For example, if I type 'sea', it would update the search term to be ' se'. Then, when I try to delete the value, it is offset the other direction, so deleting ' se' ends with 's', which can't be deleted.
(Here's a link to the app in progress: https://vibrant-yonath-715bf2.netlify.com/allpokemon. The full search functionality isn't working quite yet. I'm pretty new at this.)
import React, { Component } from 'react';
import Pokemon from './Pokemon';
class PokemonList extends Component {
constructor(props) {
super(props);
this.state = {
pokemonList: [],
searchTerm: '',
fetched: false,
loading: false
};
this.updateResults = this.updateResults.bind(this);
}
componentWillMount() {
this.setState({
loading: true
});
fetch('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(res => res.json())
.then(response => {
this.setState({
pokemonList: response.results,
loading: true,
fetched: true
});
});
}
handleSearchTermChange = (
event: SyntheticKeyboardEvent & { target: HTMLInputElement }
) => {
this.setState({ searchTerm: event.target.value });
this.updateResults();
};
updateResults() {
const filteredList = this.state.pokemonList.filter(
pokemon =>
pokemon.name.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0
);
console.log(this.state.searchTerm);
this.setState({
pokemonList: filteredList
});
}
render() {
const { fetched, loading, pokemonList } = this.state;
let content;
if (fetched) {
content = (
<div className="flex-grid">
{pokemonList.map((pokemon, index) => (
<Pokemon key={pokemon.name} id={index + 1} pokemon={pokemon} />
))}
</div>
);
} else if (loading && !fetched) {
content = <p> Loading ...</p>;
} else {
content = <div />;
}
return (
<div>
<input
onChange={this.handleSearchTermChange}
value={this.state.searchTerm}
type="text"
placeholder="Search"
/>
{content}
</div>
);
}
}
export default PokemonList;
setState is asynchronous, so your this.state.searchTerm is not updated when you call updateResults. You could e.g. filter the array in render instead.
Example
class App extends Component {
state = {
pokemonList: [
{ name: "pikachu" },
{ name: "bulbasaur" },
{ name: "squirtle" }
],
searchTerm: ""
};
changeSearchTerm = event => {
this.setState({ searchTerm: event.target.value });
};
render() {
const { pokemonList, searchTerm } = this.state;
const filteredList = pokemonList.filter(pokemon =>
pokemon.name.toUpperCase().includes(searchTerm.toUpperCase())
);
return (
<div>
<input value={searchTerm} onChange={this.changeSearchTerm} />
{filteredList.map(pokemon => <div>{pokemon.name}</div>)}
</div>
);
}
}
I think the problem is that you call this.updateResults();
and then calling this.setState({ searchTerm: event.target.value }); instead of using the callback function for setState.
For example:
this.setState({ searchTerm: event.target.value }, () => this.updateResults());
Hope I got it right.
Update:
Also I see many problems in your code, for example, why you update the list with a filtered list? you don't need to do that:
this.setState({
pokemonList: filteredList
});
Instead of updating the results in the state, you simply need to render the filtered list... meaning your state stay with the original list, also your filterd value, just in the render you pass the filtered list..
I'm having issues with setting this.setState from within my API call. If I console.log the stocks array inside the axios call the data is available in the array. It is not available outside if it.
Is the problem because this.setState is merging objects? I'm having a hard time conceptualizing what is happening here. How do I fix this problem so I can pass the contents to props?
import React, { Component } from 'react';
import axios from 'axios';
import SearchBar from './components/search_bar';
import StockList from './components/StockList';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
stocks: [],
term: null,
value: ''
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value
});
}
handleClick(e) {
if(e) e.preventDefault();
this.setState({
value: '',
term: this.state.value
});
let term = this.state.value;
const key = 'F41ON15LGCFM4PR7';
const url = `https://www.alphavantage.co/query?function=BATCH_STOCK_QUOTES&symbols=${term}&apikey=${key}`;
axios.get(axios.get(url)
.then(res => {
let stocks = Array.from(res.data['Stock Quotes']).map((stock) => [{symbol: stock['1. symbol'], price: stock['2. price'], volume: stock['3. volume'], timestamp: stock['4. timestamp']}]);
this.setState((state, props) => {
return [...this.state.stocks]
})
})
.catch(error => console.log(error))
)
}
render () {
let stocks = this.state.stocks;
const value = this.state.value;
return (
<div className="App">
<h1>Stock Search</h1>
<SearchBar value={ value }
onChange={ this.handleChange }
onClick={ this.handleClick }/>
<StockList stockItems={ stocks }/>
</div>
);
}
}
export default App;
Your setState there is the issue, it's messing up the structure of your state.
this.setState((state, props) => {
return [...this.state.stocks]
});
Should be either:
this.setState({
// set stocks to that array you parsed from the axios response
stocks
});
or
this.setState((state, props) => {
return {
...state,
// set stocks to that array you parsed from the axios response
stocks
};
});
I suggest that because you're accessing the stocks via this.state.stocks in your render
I’m using Material UI v1.0 beta.26 and I’m facing an issue with the dropdown component, in this new version you have to use the Select component combined with MenuItem.
My dropdown is populated when the app is render but when I choose any option from it I’m getting the following error:
And this is my code:
import React from 'react';
import Select from 'material-ui/Select';
import {MenuItem, MenuIcon} from 'material-ui/Menu';
//CONSTANTS
import {CREATE_LS_DISCOUNT_TYPE_DD} from './commons/constants';
import {CREATE_LS_OFFER_TYPE_DD} from './commons/constants';
import cr from '../styles/general.css';
export default class ExampleDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
DiscountTypeData: [],
OfferTypeData: [],
DiscountTypeState: '',
OfferTypeState: ''
};
this.renderDiscountTypeOptions = this.renderDiscountTypeOptions.bind(this);
this.renderOfferTypeOptions = this.renderOfferTypeOptions.bind(this);
this.handleChangeDiscountType = this.handleChangeDiscountType.bind(this);
this.handleChangeOfferType = this.handleChangeOfferType.bind(this);
}
componentDidMount() {
fetch(CREATE_LS_DISCOUNT_TYPE_DD)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
DiscountTypeData: findResponse.discountTypes,
});
});
}
handleChangeDiscountType(event, index, value) {
this.setState({ DiscountTypeState: (value)});
fetch(CREATE_LS_OFFER_TYPE_DD)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
}
handleChangeOfferType(event, index, value) {
this.setState({ OfferTypeState: event.target.value });
}
renderDiscountTypeOptions() {
return this.state.DiscountTypeData.map((dt) => {
return (
<MenuItem
key={dt.id}
value={dt.text}>
{dt.text}
</MenuItem>
);
});
}
renderOfferTypeOptions() {
return this.state.OfferTypeData.map((dt) => {
return (
<MenuItem
key={dt.offerTypeCode}
value={dt.offerTypeDesc}>
{dt.offerTypeDesc}
</MenuItem>
);
});
}
render() {
return (
<div className={cr.container}>
<div>
<Select
value={this.state.DiscountTypeState}
onChange={this.handleChangeDiscountType}>
{this.renderDiscountTypeOptions()}
</Select>
</div>
<br/>
<div>
<Select
value={this.state.OfferTypeState}
onChange={this.handleChangeOfferType}>
{this.renderOfferTypeOptions()}
</Select>
</div>
</div>
);
}
}
in the following method (handleChangeDiscountType) if I leave it like this "this.setState({ DiscountTypeState: value})" I got the error in the screenshot above but if I change that line like this "this.setState({ DiscountTypeState: event.target.value}) it works so I want to understand why
handleChangeDiscountType(event, index, value) {
this.setState({ DiscountTypeState: value});
fetch(CREATE_LS_OFFER_TYPE_DD + 1)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
}
also what I want to do is to get the index of my selection in order to pass it to my second web service call but I don't know how to do it, in the previous version of Material UI I just put "index" and works but in the new version ain't work so I want to know a new way to add that parameter.
fetch(CREATE_LS_OFFER_TYPE_DD + PASS INDEX HERE)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
I'll appreciate any help with this..
The onChange handler provided to Select is invoked with a target that is enriched with value and name, so you need to pull value from event.target:
handleChangeDiscountType(event) {
const {
DiscountTypeData
} = this.state;
// you're using the text property as the value, but you should probably use its id
// still, here's how you'd find the item using the selected item's value
const selectedDiscount = DiscountTypeData.filter(
discount => discount.text === event.target.value,
);
// use a templated literal to specify the endpoint with the selected item's id
fetch(`${CREATE_LS_OFFER_TYPE_DD}/${selectedDiscount.id}`)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes,
});
});
}
The reason your code was not working is because onChange is not invoked with a third parameter, so your use of value was setting state to undefined.
For more information, see the Selects demo.