I've got a <select> element where I'm pulling the options from a Rails data model. This is ok, but produces a bog-standard HTML select dropdown.
However, I'm wanting to use react-select component and this is where I'm struggling. I am able to render the react-select dropdown, but the options are blank. I don't have any errors in the console, and I can see the 51 items in my array in React-Dev-Tools.
This is the code that produces the basic HTML dropdown.
import React from 'react';
import axios from 'axios';
import Select from 'react-select';
class Country extends React.Component {
constructor(props) {
super(props)
this.state = {
countries: []
}
}
getCountries() {
axios.get(`/countries.json`)
.then(res => {
const countries = res.data;
this.setState({ countries });
})
.catch(error => console.log(error))
}
componentDidMount() {
this.getCountries()
}
render() {
return (
<div className="container">
<select className="taskList">
{this.state.countries.map((country) => {
return (
<option key={country.id} value={country.id}>{country.country_name}</option>
)
})}
</select>
</div>
)
}
}
export default Country
This is the code I'm trying for the react-select, and doesn't work
import React from 'react';
import axios from 'axios';
import Select from 'react-select';
class Country extends React.Component {
constructor(props) {
super(props)
this.state = {
countries: []
}
}
getCountries() {
axios.get(`/countries.json`)
.then(res => {
const countries = res.data;
this.setState({ countries });
})
.catch(error => console.log(error))
}
componentDidMount() {
this.getCountries()
}
render() {
return (
let countryItems = this.state.countries.map((country) =>
<option key={country.id} value={country.id}>{country.country_name}</option>
);
return (
<div className="container">
<label>Country</label>
<Select id="country" name="coffee_beans[country_id]" options={countryItems} />
</div>
)
}
}
export default Country
Your options to react-select component should be an array of objects:
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
];
Now you are passing an array of components. So instead of
let countryItems = this.state.countries.map((country) =>
<option key={country.id} value={country.id}>
{country.country_name} .
</option>
);
try something along this:
let countryItems = this.state.countries.map(country => ({
value: country.id,
label: country.country_name
});
Related
I have been building a trivia game using the Open Trivia API and I am getting an error that I have been trying to figure out but am stuck on. It says: Invalid value for prop value on tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.
I will include my code pages. Thank you in advance for you help.
App.js page:
import React from 'react'
import './App.css'
import axios from 'axios'
import Questions from './components/questions'
import CategorySelector from './components/CategorySelector'
class App extends React.Component {
constructor () {
super()
this.state = {
categories: [],
selectedCategory: null
}
this.selectedCategory = this.selectedCategory.bind(this)
}
componentDidMount () {
axios
.get('https://opentdb.com/api_category.php')
.then(response => {
console.log(response.data)
this.setState({
categories: response.data.trivia_categories
})
})
}
selectedCategory (category) {
this.setState({ selectedCategory: category })
}
render () {
const { categories, selectedCategory } = this.state
return (
<div className='App'>
<h2>Trivia Game</h2>
{
selectedCategory
? <Questions selectedCategory={selectedCategory} />
: (
<CategorySelector
categories={categories}
onSelect={event => selectedCategory}
selectedCategory={this.selectedCategory}
/>
)
}
</div>
)
}
}
export default App
CategorySelector.js page:
import React from 'react'
class CategorySelector extends React.Component {
render () {
const { categories, selectedCategory, onSelect } = this.props
return (
<div className='CategorySelector'>
<select
value={selectedCategory} onChange={onSelect}
>
<option value=''>-- No category selected --</option>
{categories.map(category => (
<option value={selectedCategory} key={category.id}>{category.name}</option>
))}
</select>
</div>
)
}
}
export default CategorySelector
Question.js page
import React from 'react'
import axios from 'axios'
class Questions extends React.Component {
constructor () {
super()
this.state = {
questions: [],
currentQuestionIndex: 0
}
this.handleNextQuestion = this.handleNextQuestion.bind(this)
}
componentDidMount () {
const qUrl = `https://opentdb.com/api.php?amount=3&category=${this.props.selectedCategory.id}`
axios
.get(qUrl)
.then(response => {
console.log(response.data)
this.setState({
questions: response.data.results
})
})
}
handleNextQuestion () {
this.setState({ currentQuestionIndex: this.state.currentQuestionIndex + 1 })
}
render () {
const { selectedCategory } = this.props
const { questions, currentQuestionIndex } = this.state
const currentQuestion = questions[currentQuestionIndex]
let answers = []
if (currentQuestion) {
answers = currentQuestion.incorrect_answers.concat([currentQuestion.correct_answer])
}
return (
<div className='Questions'>
<h2>{selectedCategory.name} Questions</h2>
{currentQuestion && (
<div>
<h3>{currentQuestion.questions}</h3>
<div>
{answers.map((answer, index) => <p key={index}>{answer}</p>)}
</div>
</div>
)}
{(currentQuestionIndex < questions.length - 1) &&
<button onClick={this.handleNextQuestion}>Next Question</button>}
</div>
)
}
}
export default Questions
While passing the prop, you're referring the state variable as this.selectedCategory
Change this line
selectedCategory={this.selectedCategory}
to
selectedCategory={selectedCategory}
I'm trying to create a web-app with the front end in React and backend in Flask. I have a dropdown which gets populated by the flask JSON which is basically a list of companies. All total there are around 5 components, the first one is the App.js, second is the CompanySelection.js, the third one is the Chart.js where I want to return my graphs and all.
So in theCompanySelection.js when I change the dropdown selection the updated company name does not go into Charts.js, i.e. the other component. I guess when this part gets solved similarly I can pass the values from one component to the other easily.
These are my three code files:
App.js
import React from "react";
import { CompanyContextProvider } from "./context";
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
import Header from "./Header";
import CompanySelection from "./CompanySelection/CompanySelection.js";
import Charts from "./Charts/Chart.js";
import axios from 'axios';
class App extends React.Component{
state = {
companies: [],
firstCompany: {},
firstCompanyName: ''
};
componentDidMount() {
fetch('http://127.0.0.1:5001/algo/loc')
.then(res => res.json())
.then(data => {
this.setState({companies: data,
firstCompany: data[0],
firstCompanyName: data[0].value}, () =>
console.log(this.state.companies, this.state.firstCompany, this.state.firstCompanyName));
console.log('')
}).catch(function (error) {
console.log(error);
});
}
selectedValueHandler = (selectedValue) => {
this.setState({
firstCompanyName: selectedValue
})
}
render() {
const { selectedValue } = this.state.firstCompanyName;
console.log('change value',selectedValue)
return (
<div className="app">
<Header/>
<CompanySelection companies= {this.state.companies} selectedCompany={this.state.firstCompany} setSelectedCompany={this.state.firstCompanyName} selectedValueHandler = {this.selectedValueHandler}/>
<Charts companies= {this.state.companies} selectedCompany={this.state.firstCompany} setSelectedCompany={selectedValue}/>
</div>
);
}
} ;
export default App;
CompanySelection.js
import { h, render, Component} from 'preact';
import style from './style.css';
import { useContext } from "preact/hooks";
import { CompanyContext } from "../context";
class CompanySelection extends Component {
constructor(props)
{
super(props);
}
render(_, { value }) {
const companies = this.props.companies;
const selectedCompany = this.props.selectedCompany;
const setSelectedCompany = this.props.setSelectedCompany;
var onChange = (e) =>{
console.log("In on change");
this.setState({ value: e.target.value });
const setSelectedCompany = e.target.value;
console.log("Selected", e.target.value);
const companies = this.props.companies;
const selectedCompany = this.props.selectedCompany;
this.props.selectedValueHandler(e.target.value);
}
if (typeof companies !== 'undefined')
{
var options = companies.map((comp) =>
<option
key={comp.label}
value={comp.value}
>
{comp.label}
</option>
);
}
else {
var options = [{value: 'A', label: 'B'}].map((comp) =>
<option
key={comp.label}
value={comp.value}
>
{comp.label}
</option>
);
}
return (
<fragment class={style.fragment}>
<label class={style.label}> Company </label>
<select value={value} onChange={onChange} class={style.dropdown}>
{options}
</select>
</fragment>
);
}
}
render(<CompanySelection />, document.body);
export default CompanySelection;
Chart.js
import { h, render, Component } from 'preact';
import style from './style.css';
import { VictoryChart, VictoryLine, VictoryScatter, VictoryLabel} from 'victory';
import { useContext } from "preact/hooks";
import { CompanyContext } from "../context";
class Charts extends Component {
constructor(props)
{
super(props);
}
render(_, { value }) {
const companies = this.props.companies;
const selectedCompany = this.props.selectedCompany;
const setSelectedCompany = this.props.setSelectedCompany;
console.log('list of companies chart', companies)
console.log('chart input', setSelectedCompany)
if (typeof selectedCompany !== 'undefined') {
var comp = selectedCompany;
}
else {
var comp = '';
}
console.log("comp", comp);
return (
<fragment>
<div class={style.chart}>
<VictoryChart domain={[0, 10]}>
<VictoryLabel text={comp} x={225} y={30} textAnchor="middle"/>
<VictoryLine
style={{ data: { stroke: "blue", strokeWidth: 3 } }}
y={(d) => d.x}
/>
<VictoryScatter
symbol="star"
size={8}
style={{ data: { fill: "red" }}}
data={[{ x: 5, y: 5 }]}
/>
<VictoryScatter
symbol="circle"
size={8}
style={{ data: { fill: "red" }}}
data={[{ x: 7, y: 7 }]}
/>
</VictoryChart>
</div>
</fragment>
);
}
}
render(<Charts />, document.body);
export default Charts;
I have taken reference of this code from this stackoverflow post: How to pass data from one component to another component in onchange using React js
However when I see the output of this line of code: const { selectedValue } = this.state.firstCompanyName;
console.log('change value',selectedValue)
I get that change value undefined, which means the values are not getting passed on. I'm very new to react and haven't been able to solve this yet. Any help is much appreciated.
P.S. The components pass on well to the <CompanySelection..../>
However when I see the output of this line of code: const { selectedValue } = this.state.firstCompanyName;
console.log('change value',selectedValue)
It will be undefined, here you are trying to destructure selectedValue from a string firstCompanyName, as long as this.state.firstCompanyName is not an object with a key named selectedValue it will be undefined. Instead do this.
const selectedValue = this.state.firstCompanyName;
console.log('change value',selectedValue)`
I am trying to make live search for name in table but i can't make live search i don't know how to do this i wrote my code like this as i mentioned please help me how to make live search on name field foe table and in Search Page i used onSubmit={this.props.loaddata like this thanks
import React, { Component } from "react";
import Search from "../../views/Cars/Search";
class Search1 extends Component {
constructor(props) {
super(props);
this.state = {
query: []
};
}
// Get Data from filter date
getData = async e => {
try {
const search = e.target.elements.search.value;
e.preventDefault();
const res = await fetch(`https://swapi.co/api/people/?search=${search}`);
const query = await res.json();
console.log(query);
this.setState({
query: query.results
});
} catch (e) {
console.log(e);
}
};
async componentDidMount() {
// let authToken = localStorage.getItem("Token");
try {
const res = await fetch(`https://swapi.co/api/people/`);
const query = await res.json();
// console.log(movie);
this.setState({
query: query.results
});
} catch (e) {
console.log(e);
}
}
render() {
const options = this.state.query.map(r => <li key={r.id}>{r.name}</li>);
return (
<div>
<Search loaddata={this.getData} />
{options}
</div>
);
}
}
export default Search1;
Genrally You can try React-Search
import Search from 'react-search'
import ReactDOM from 'react-dom'
import React, { Component, PropTypes } from 'react'
class TestComponent extends Component {
HiItems(items) {
console.log(items)
}
render () {
let items = [
{ id: 0, value: 'ruby' },
{ id: 1, value: 'javascript' },
{ id: 2, value: 'lua' },
{ id: 3, value: 'go' },
{ id: 4, value: 'julia' }
]
return (
<div>
<Search items={items} />
<Search items={items}
placeholder='Pick your language'
maxSelected={3}
multiple={true}
onItemsChanged={this.HiItems.bind(this)} />
</div>
)
}
}
Made few changes to your component. Send e.target.value from your child component
class Search1 extends Component {
constructor(props) {
super(props);
this.state = {
query: []
};
}
// Get Data from filter date
getData = search => {
const url = `https://swapi.co/api/people${search ? `/?search=${search}` : ``}`;
// e.preventDefault();
fetch(url)
.then(res => res.json())
.then(data =>
this.setState({
query: data.results || []
})).catch(e => console.log(e));
};
async componentDidMount() {
// let authToken = localStorage.getItem("Token");
this.getData();
}
render() {
const options = this.state.query.map(r => <li key={r.id}>{r.name}</li>);
return (
<div>
<Search loaddata={this.getData} />
{options}
</div>
);
}
}
export default Search1;
For Gettind Data from Api you can follow this code of react-search
import Search from 'react-search'
import ReactDOM from 'react-dom'
import React, { Component, PropTypes } from 'react'
class TestComponent extends Component {
constructor (props) {
super(props)
this.state = { repos: [] }
}
getItemsAsync(searchValue, cb) {
let url = `https://api.github.com/search/repositories?q=${searchValue}&language=javascript`
fetch(url).then( (response) => {
return response.json();
}).then((results) => {
if(results.items != undefined){
let items = results.items.map( (res, i) => { return { id: i, value: res.full_name } })
this.setState({ repos: items })
cb(searchValue)
}
});
}
render () {
return (
<div>
<Search items={this.state.repos}
multiple={true}
getItemsAsync={this.getItemsAsync.bind(this)}
onItemsChanged={this.HiItems.bind(this)} />
</div>
)
}
I have two working react-select drop downs on my page, one that allows the user to select A or B, and one that allows them to choose multiple items from "blue, yellow , red".
When they have chosen these items, I want to use them. For now I just want to check the values that have been selected, so I'm just printing them to screen. For the single selection drop down I have used the example code from the github successfully. This is as follows:
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'a', label: 'a' },
{ value: 'b', label: 'b' },
];
class App extends React.Component {
state = {
selectedOption: null,
}
handleChange = (selectedOption) => {
this.setState({ selectedOption });
document.write(`Option selected:`, selectedOption.value); //this prints the selected option
}
render() {
const { selectedOption } = this.state;
return (
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
//isMulti //added when the user can pick more than one
/>
);
}
}
My question is how do I successfully do this for the multi option? The user can select as many as they wish, but an 'undefined' error is thrown when it prints the option that has been selected. I think this is because the option is stored in an array but I'm not sure.
Thanks all.
You need to change the handleChange to deal with the isMulti. Here's an example:
import React, { Component } from 'react';
import { render } from 'react-dom';
import Select from 'react-select';
const options = [
{ value: 'a', label: 'a' },
{ value: 'b', label: 'b' },
];
class App extends React.Component {
state = {
selectedOptions: [],
}
handleChange = (selectedOptions) => {
this.setState({ selectedOptions });
}
render() {
const { selectedOptions } = this.state;
return (
<React.Fragment>
<Select
isMulti
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
{selectedOptions.map(o => <p>{o.value}</p>)}
</React.Fragment>
);
}
}
render(<App />, document.getElementById('root'));
Here's a working example: https://stackblitz.com/edit/react-czf4ib
If you are using react hooks, here is how you can get the selected values from the react-select with isMulti prop.
import React, {useState} from 'react';
import Select from 'react-select';
import { useForm } from 'react-hook-form';
import { handleSubmit } = useForm();
const options = [
{ value: 'a', label: 'a' },
{ value: 'b', label: 'b' },
];
const FormComponent = (props) => {
const [selectedOptions, setSelectedOptions] = useState([]);
const handleChange = (options) => {
setSelectedOptions(options);
};
const onSubmit = (formData, event) => {
console.log("Form Data: ", formData)
console.log("Selected Options: ", selectedOptions)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Select
isMulti = {true}
options={options}
closeMenuOnSelect={false}
onChange={handleChange}/>
<button type="submit">Save</button>
</form>
);
}
Hope it helps!
For store value in array try this one
[ "purple", "red", "blue" ]
handleChange = (selectedOptions) => {
let catArray = [];
selectedOptions.map(o =>
catArray.push(o.value)
);
this.setState({selectedOptions:catArray});
}
onChange={this.handleChange}
I'm trying to add and remove a dropdown field on click of two buttons:
one 'Add' button which adds/creates a new dropdown field (predefined with options & values)
A 'Remove' button next to each dropdown (in order to remove them individually)
This is a multi page app so I'm using React Router to navigate from one page to another - so to keep the state I'm using Redux.
I managed to add and remove the dropdown fields on click of the buttons - but I'm facing two issues:
I'm falling to save the dropdowns to the store, so if I navigate to another page and come back, the added dropdowns are gone from the page
Anytime a new dropdown is added, the option of the selects is reset
Here is my code so far:
import React from 'react'
import { connect } from 'react-redux'
import { addData, saveSelect, removeSelect } from '../actions.js'
class AddSelectField extends React.Component {
constructor(props){
super(props);
this.state = {
inputSelect: []
}
}
saveData = (e) => {
let data = {}
data[e.target.name] = e.target.value
this.context.store.dispatch(
addData(data)
)
}
addInput = () => {
const inputSelect = this.state.inputSelect.concat(this.renderDropDown);
this.setState({ inputSelect });
this.context.store.dispatch(
saveSelect(this.state.inputSelect)
)
}
removeInput = (item) => {
let index = this.state.inputSelect.indexOf(item);
let newInputSelect = this.state.inputSelect;
newInputSelect.splice(index,1);
this.setState({
inputSelect: newInputSelect
});
this.context.store.dispatch(
removeSelect(newInputSelect)
)
}
renderDropDown = (el, index) => {
return(
<div>
<select
key={index}
name={'document-'+ index}
value={'document-'+ index}
onChange = {this.saveData}
>
<option value="0">Please Select</option>
<option value="1">Australia</option>
<option value="2">France</option>
<option value="3">United Kingdom</option>
<option value="4">United States</option>
</select>
<button onClick={ this.removeInput }>Remove</button>
</div>
)
}
render(){
return(
<div>
<button onClick={ this.addInput }>Add</button>
<div className="inputs">
{this.state.inputSelect.map(this.renderSelect)}
</div>
</div>
)
}
}
class AccountUpgrade extends React.Component {
constructor(props) {
super(props);
}
continueClick() {
this.context.router.push('/AccountUpgrade/Confirmation/')
}
render(){
return (
<div>
<div className="row">
<AddSelectField />
<ButtonRow
primaryProps={{
children: 'Continue',
onClick: this.continueClick.bind(this)
}} />
</div>
</div>
)
}
}
AccountUpgrade.contextTypes = {
store: React.PropTypes.object.isRequired,
router: React.PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.EligibleAbout
}
}
const EligibleAbout = connect(mapStateToProps)(AccountUpgrade)
export default EligibleAbout
action.js
export const ADD_DATA = 'ADD_DATA'
export const ADD_SELECT = 'ADD_SELECT'
export const REMOVE_SELECT = 'REMOVE_SELECT'
export function addData(data) {
return { type: ADD_DATA, data }
}
export function saveSelect(data) {
return { type: ADD_SELECT, data }
}
export function removeSelect(data) {
return { type: REMOVE_SELECT, data }
}
reducer.js
import ObjectAssign from 'object.assign'
import { combineReducers } from 'redux'
import { ADD_DATA, ADD_SELECT, REMOVE_SELECT } from './actions'
function EligibleAbout(state = {}, action){
switch (action.type){
case ADD_DATA:
return ObjectAssign({}, state, action.data)
case ADD_SELECT:
return ObjectAssign({}, state, action.data)
case REMOVE_SELECT:
return ObjectAssign({}, state, action.data)
default:
return state
}
}
const FormApp = combineReducers({
EligibleAbout
})
export default FormApp
You are managing state in the component as well as in the reducer. In the render function, you are always taking values of inputSelect from the state which is blank initially. You are not using values stored in the reducer for rendering purpose that's why you are not getting it on coming back.
Do not store inputSelect in your component state. Just store that in reducer and use inputSelect from reducer for rendering purpose.
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import uuidV4 from 'uuid/v4'
class AddSelectField extends React.Component {
static propTypes = {
ids: PropTypes.array,
removeSelect: PropTypes.func,
saveSelect: PropTypes.func,
}
static defaultProps = {
ids: [],
}
saveData = (e) => {
// Just do your stuff here.
}
addInput = () => {
this.props.saveSelect(uuidV4())
}
removeInput = index => {
this.props.removeSelect(index)
}
renderDropDown = (id, index) => {
return (
<div>
<select
key={id}
name={id}
onChange={this.saveData}
>
<option value="0">Please Select</option>
<option value="1">Australia</option>
<option value="2">France</option>
<option value="3">United Kingdom</option>
<option value="4">United States</option>
</select>
<button
onClick={() => {
this.removeInput(index)
}}>
Remove
</button>
</div>
)
}
render() {
const ids = this.props.ids
return(
<div>
<button onClick={ this.addInput }>Add</button>
<div className="inputs">
{ids.map(this.renderDropDown)}
</div>
</div>
)
}
}
const mapStateToProps = state => {
const eligibleAbout = state.EligibleAbout
return {
ids: eligibleAbout.ids,
}
}
const EligibleAbout = connect(mapStateToProps, { saveSelect, removeSelect })(AddSelectField)
export default class AccountUpgrade extends React.Component {
continueClick() {
// do your stuff here.
}
render() {
return (
<div>
<div className="row">
<EligibleAbout />
</div>
</div>
)
}
}
Updated Reducer is:
function eligibleAbout(state = { ids: [] }, action = {}){
switch (action.type) {
case ADD_DATA:
// set according to requirement.
case ADD_SELECT:
return {
...state,
ids: [].concat(state.ids, action.data),
}
case REMOVE_SELECT:
return {
...state,
ids: state.ids.filter((id, index) => (index !== action.data)),
}
default:
return state
}
}
If I had been at your place, I would do it like this except state management. I would recommend using immutable will be a good option for state management.
Note: I have only implemented adding and removing input functionality. Not clear about your saveData function requirement.