React change state based on select option - javascript

I need to store the distance value in state. It should equal the distance passed as props + the distance selected by user. How to do this?
class Distance extends React.Component {
constructor(props) {
super(props);
this.state = {
distance: 0
};
}
onChange = e => {
}
render() {
return (
<div>
<p>{this.props.distance}</p>
<select onChange={this.onChange}>
<option>30km</option>
<option>50km</option>
<option>70km</option>
</select>
</div>
);
}
}

Using functional components, you can do this:
const Distance = () => {
const [distance, setDistance] = useState("");
return (
<div>
<p>{distance}</p>
<select onChange={(e) => setDistance({distance: e.target.value})}>
<option value="30">30km</option>
<option value="50">50km</option>
<option value="70">70km</option>
</select>
</div>
);
};
export default Distance;
In a case where you have multiple inputs, and only distance is a select input, you can do this to update distance while maintaining the values of other inputs:
const Distance = () => {
const [input, setInput] = useState({
distance: "",
time: "",
place: "",
});
return (
<div>
<p>{distance}</p>
<select onChange={(e) =>
setInput({ ...input, distance: e.target.value }}
>
<option value="30">30km</option>
<option value="50">50km</option>
<option value="70">70km</option>
</select>
</div>
);
};
export default Distance;

First add value attributes to your <option> elements, and then access the use the value of the <select> via e.currentTarget.value in your onChange handler like so:
class Distance extends React.Component {
constructor(props) {
super(props);
this.state = {
distance: 0
};
}
onChange = e => {
// Extract value of select like so. Use parseInt for
// improved type safety
const valueSelectedByUser = parseInt(e.target.value);
// Update distance in state via setState()
this.setState({ distance : this.props.distance + valueSelectedByUser });
}
render() {
return (
<div>
<p>{this.props.distance}</p>
<select onChange={this.onChange}>
<option value="30">30km</option>
<option value="50">50km</option>
<option value="70">70km</option>
</select>
</div>
);
}
}

You should include value for select and handle onChange event:
class Distance extends React.Component {
constructor(props) {
super(props);
this.state = {
distance: 0
};
}
onChange = e => {
this.setState({
distance: this.props.distance ? this.props.distance + e.target.value : e.target.value
});
}
render() {
return (
<div>
<p>{this.props.distance}</p>
<select onChange={this.onChange}>
<option value="30">30km</option>
<option value="50">50km</option>
<option value="70">70km</option>
</select>
</div>
);
}
}

you can simply do this like this, I have converted your code to functional components and also modified it, try this.
const Distance = () => {
const [distance, setDistance] = useState("");
return (
<div>
<p>{distance}</p>
<select onChange={(e) => setDistance(e.target.value)}>
<option value="30">30km</option>
<option value="50">50km</option>
<option value="70">70km</option>
</select>
</div>
);
};
export default Distance;

Related

How to apply filter component to my products

Good day,
i would like to apply my filter component to my products. So when the user clicks on the select options, he can choose the size and only the products of the selected size will appear on the screen. I have created the logic, but i just dont know how to apply it.
code FilterAction:
const Filterproducts = (products,size) => (dispatch) => {
return dispatch({
type:FILTER_PRODUCTS_BY_SIZE,
payload: {
size:size,
items:size === ''? products: products.filter(a=> a.availableSizes.indexOf(size.toUpperCase())>= 0)
}
})
}
Code FilterReducer:
function producListReducer(state = {products: [], filteredItems: [], size: ''}, action){
switch (action.type){
// case is like the if statement
//getting product
case PRODUCT_LIST_REQUEST:
return{loading: true};
// when products are loaded
case PRODUCT_LIST_SUCCESS:
return{loading:false, products: action.payload};
//when err occurs
case PRODUCT_LIST_FAIL:
return{loading: false, error: action.payload};
case FILTER_PRODUCTS_BY_SIZE:
return{...state, filteredItems: action.payload.products, size: action.payload.size}
default:
return state
}
}
code Filter component:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {Filterproducts} from '../../actions/productActions'
class Filter extends Component {
constructor(){
super()
this.state = {
}
}
render(){
return(
<div className="filter">
<label>
Order:
<select>
<option value="lowest">Lowest to Highest</option>
<option value="highest">Highest to Lowest</option>
</select>
</label>
<label>
Size:
<select
className="size"
onChange={(e)=> this.props.Filterproducts(this.props.products, e.target.value)}
>
<option value="">ALL</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
</select>
</label>
</div>
)
}
}
const mapStateToProps = state => ({
products: state.products.items,
size: state.products.size
})
export default connect(mapStateToProps,{Filterproducts})(Filter)
Here is an example. Assuming product has name.
const Filterproducts = (size) => (dispatch) => {
return dispatch({
type:FILTER_PRODUCTS_BY_SIZE,
payload: {
size,
}
})
}
function producListReducer(state = {products: [], filteredItems: [], size: ''}, action){
switch (action.type){
// case is like the if statement
//getting product
case PRODUCT_LIST_REQUEST:
return{loading: true};
// when products are loaded
case PRODUCT_LIST_SUCCESS:
return{loading:false, products: action.payload};
//when err occurs
case PRODUCT_LIST_FAIL:
return{loading: false, error: action.payload};
case FILTER_PRODUCTS_BY_SIZE:
const { size } = action.payload
const filterProduct = size === ''? state.products: state.products.filter(a=> a.availableSizes.indexOf(size.toUpperCase())>= 0)
return{...state, filteredItems:[...filterProduct], size: action.payload.size}
default:
return state
}
}
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {Filterproducts} from '../../actions/productActions'
class Filter extends Component {
render(){
return(
<div className="filter">
<label>
Order:
<select>
<option value="lowest">Lowest to Highest</option>
<option value="highest">Highest to Lowest</option>
</select>
</label>
<label>
Size:
<select
className="size"
onChange={(e)=> this.props.Filterproducts(e.target.value)}
>
<option value="">ALL</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
</select>
</label>
{ filteredItem.length && filteredItems.map( product => {
return (<> {product.name} </>
}
</div>
)
}
}
const mapStateToProps = state => ({
filteredItems: state.filteredItems,
})
export default connect(mapStateToProps,{Filterproducts})(Filter)

Trouble with Axios post request in basic MERN stack app

I want to create an axios post request that sends the id's of a bull and a heifer chosen by a user to my server to calculate traits (in this case milk production) for their offspring. I'm trying to trigger it after the submit button is clicked. I think I'm not sending the id's as properly formatted params for the server to process.
import React, { Component} from 'react';
import axios from 'axios';
class Dropdown extends Component {
constructor (props) {
super(props)
this.handleInputChange = this.handleInputChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
bullId: '',
heiferId: ''
}
}
//this updates state with new bull & heifer
handleInputChange(event) {
const target = event.target;
const value = target.value;
target.clicked = target.value;
const name = target.name;
console.log(target.name)
;
this.setState({
[name]: value
});
console.log(this.state);
}
handleChange = (event) => {
var bull = event.target.value
var heifer = event.target.value
console.log(heifer)
console.log(bull)
};
onSubmit(e) {
e.preventDefault();
const pairing = {
heifer: this.state.heiferId,
bull: this.state.bullId
}
console.log(pairing)
axios.post('http://localhost:8000/traits/:bullId/:heiferId', pairing)
.then(res => console.log(res.data));
this.setState({
bullId:"",
heiferId:""
})
}
render() {
return (
<div>
<form>
<label>Bulls
<select
name={"bullId"}
value ={this.state.bullId}
onChange= {this.handleInputChange}>
<option value="5defc2b5b9283d6de054e0f0">Buddy</option>
<option value="5defc2b5b9283d6de054e0f1">Cooper</option>
<option value="5defc2b5b9283d6de054e0f2">Maxwell</option>
<option value="5defc2b5b9283d6de054e0f3">Gus</option>
<option value="5defc2b5b9283d6de054e0f4">Paul</option>
<option value="5defc2b5b9283d6de054e0f5">Phil</option>
</select>
</label>
<br />
<label>Heifers
<select
name={"heiferId"}
value={this.state.heiferId}
onChange= {this.handleInputChange}>
<option value="5defc49cb9283d6de054e0f6">Sally</option>
<option value="5defc49cb9283d6de054e0f7">Patches</option>
<option value="5defc49cb9283d6de054e0f8">Maxine</option>
<option value="5defc49cb9283d6de054e0f9">Peach</option>
<option value="5defc49cb9283d6de054e0fa">Paula</option>
<option value="5defc49cb9283d6de054e0fb">Flower</option>
</select>
</label>
</form>
<button onClick={this.onSubmit}>submit</button>
</div>
)}
}
export default Dropdown;
Heifer.findOne({_id: req.params.heiferId}).then(function(heifer){
Bull.findOne({_id: req.params.bullId}).then(function(bull){
console.log(bull);
console.log(heifer);
let heiferMilkProduction = heifer.milkProduction;
let bullMilkProduction = bull.milkProduction;
if (heiferMilkProduction > bullMilkProduction) {
heiferMilkProduction += heiferMilkProduction * .1
bullMilkProduction -= bullMilkProduction * .1
} else {
bullMilkProduction += bullMilkProduction * .1
heiferMilkProduction -= heiferMilkProduction * .1
};
const calfTraits = {
bullMilkProduction,
heiferMilkProduction
}
res.send(calfTraits);
})
})
});```
You want something like
axios.post(`http://localhost:8000/traits/${this.state.bullId}/${this.state.heiferId}`)
The :bullId syntax in a string does nothing in react, you have to build the string like any other regular string. Its used in express for the routes as a template.
You need to embedded the value of 'bullId' and 'heiferId' in your url you are using to fetch data instead of the string.
onSubmit(e) {
e.preventDefault();
const { bullId, heiferId } = this.state;
axios.post(`http://localhost:8000/traits/${bullId}/${heiferId}`, {})
.then(res => console.log(res.data));
this.setState({
bullId:"",
heiferId:""
})
}

Get all the selected options on each onChange event

I have several select element generated by a map. Aside from usingĀ  jQuery to access the dom elements is there a way to get all the selected values onChange
changed = () => {
// Keep track of all the selected options
}
[1,2,3].map(value => (
<select onChange={changed}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>)
Whenever I select an option I would like to keep track of the option selected in an array. If in the first select I chose 1 then the second one 2 I'd like to have an array [1,2] representing the options picked. If I then select the third option to be 3 then the new array should be [1,2,3].In this case I want three separate select and I want to keep track the options selected in each
Assume you use hooks in your code. This should be like
import React, { useState, useEffect } from "react";
const App = () => {
useEffect(() => {
console.log("selections: ", selections);
});
const [selections, setSelections] = useState({});
const changed = (value, e) => {
setSelections({ ...selections, [`${value}`]: e.target.value });
};
return (
<div>
{[1, 2, 3].map(value => (
<select key={value} onChange={e => changed(value, e)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
))}
</div>
);
};
export default App;
UPDATE: I updated my solution, sorry for missunderstand your questions, in case of multi dropdown list, your state should construct this way:
{ "1": 1, "2": 3, "3": 1}
Key is dropdown identifier and value is the selected option for it.
I've written examples using both React class and functional component for you.
If you want to use select with multiple values, you will have to set select multiple attribute to true. Note that select is very difficult to style and you may consider using a custom Dropdown instead.
import React from 'react';
import ReactDOM from 'react-dom';
class ClassExample extends React.Component {
state = {
value: [],
}
handleOnChange = (e) => {
const { value: selectedValue } = e.target;
const { value } = this.state;
const newValue = [].concat(value);
const index = newValue.findIndex(v => v === selectedValue);
if (index > -1) {
newValue.splice(index, 1);
} else {
newValue.push(selectedValue);
}
this.setState({ value: newValue });
}
render() {
const { value } = this.state;
return (
<div>
<select
value={value}
multiple
onChange={this.handleOnChange}
>
{[1, 2, 3].map(v => <option key={v} value={v}>{v}</option>)}
</select>
<pre>
{JSON.stringify(value, null, 2)}
</pre>
</div>
)
}
}
const FunctionExample = () => {
const [value, setValue] = React.useState([]);
const handleOnChange = (e) => {
const { value: selectedValue } = e.target;
const newValue = [].concat(value);
const index = newValue.findIndex(v => v === selectedValue);
if (index > -1) {
newValue.splice(index, 1);
} else {
newValue.push(selectedValue);
}
setValue(newValue);
}
return (
<div>
<select
value={value}
multiple
onChange={handleOnChange}
>
{[1, 2, 3].map(v => <option key={v} value={v}>{v}</option>)}
</select>
<pre>
{JSON.stringify(value, null, 2)}
</pre>
</div>
)
}
const App = () => (
<>
<label>
Class : <ClassExample />
</label>
<label>
Function : <FunctionExample />
</label>
</>
)
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
Here is a working demo: https://codesandbox.io/s/react-controlled-multiple-select-g7shd?fontsize=14&hidenavigation=1&theme=dark

How could I edit a users from this API in local state?

I need to have an edit button to edit the users first name, last name from the api but only update the input in local state. I've done a lot of research but can't find exactly what I'm looking for. I'm trying not to bring in other libraries (other than lodash possibly?). I've found a lot of other examples but its bringing in other libraries. Any suggestions would help (even on the code I have currently to help clean it up a bit.)
import React, { Component } from "react";
import axios from "axios";
import User from './User'
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
searchTerm: '',
alphabetical: 'az'
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
axios.get("https://randomuser.me/api/?results=20")
.then(response => {
console.log(response.data.results);
this.setState({ users: response.data.results });
})
.catch(error => {
console.log(error);
});
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
let sortedUsers;
if (this.state.alphabetical === "az") {
console.log("sorted");
sortedUsers = this.state.users.sort((a, b) =>
a.name.first > b.name.first ? 1 : -1
);
}
let filteredUsers = sortedUsers;
if (this.state.searchTerm)
filteredUsers = this.state.users.filter(u =>
u.name.first.startsWith(this.state.searchTerm) || u.name.last.startsWith(this.state.searchTerm)
);
const userNames = filteredUsers.map(u => {
return <User
key={u.email}
name={u.name.first}
last={u.name.last}
image={u.picture.medium}
email={u.email}
city={u.location.city}
state={u.location.state}
cell={u.cell}
/>;
});
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Search for user:
<input
type="text"
name="searchTerm"
value={this.state.searchTerm}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
<select
name="alphabetical"
value={this.state.alphabetical}
onChange={this.handleChange}>
<option selected value="az">
A to Z
</option>
<option value="za">Z to A</option>
</select>
{userNames}
</div>
);
}
}
export default App

Reactjs - retrieve attributes from event.target

I have a component which renders Input type='select': (I am using reactstrap)
import React, {Component} from 'react'
import {
Form,
FormGroup,
Input,
Button,
Col,
} from 'reactstrap'
import {
withRouter,
} from 'react-router'
import Context from '../../../../../provider'
class NewPost extends Component {
constructor(props) {
super(props)
this.state = {
subreddits: [],
subreddit_selected: '',
subreddit_id: 0,
...
}
this.handleSubredditSelect = this.handleSubredditSelect.bind(this)
}
componentDidMount() {
fetch('/api/reddit/r/')
.then(data => data.json())
.then(json => {
this.setState({
subreddits: json,
...
})
})
}
handleSubredditSelect(event) {
console.log('selected id: ',event.target.id)
this.setState({
subreddit_selected: event.target.value,
subreddit_id: event.target.id,
}, () =>
this.props.history.push(`/${this.state.subreddit_selected}/new/`)
)
}
...
render() {
return (
<Context.Consumer>
{context => {
return (
<React.Fragment>
<Form
...
>
<FormGroup row>
<Col sm={7}>
<Input
type="select"
onChange={this.handleSubredditSelect}
required
>
<option key='0' disabled selected>Select an Option</option>
{this.state.subreddits.map((subreddit) => {
return (
<option key={subreddit.id} id={subreddit.id}>{'r/' + subreddit.name}</option>
)
})}
</Input>
</Col>
</FormGroup>
...
</React.Fragment>
)
}}
</Context.Consumer>
)
}
}
export default withRouter(NewPost)
So, I have a function handleSubredditSelect which does the following:
handleSubredditSelect(event) {
this.setState({
subreddit_selected: event.target.value,
subreddit_id: event.target.id,
}, () =>
this.props.history.push(`/${this.state.subreddit_selected}/new/`)
)
}
In this function I am not getting any value for event.target.id.
I have tried event.target.key as well but that returned an empty string "".
I want to set subreddit_id in state to the selected option's ID
The selecting does not work because event.target in <select> element returns entire tree with options:
// result of event.target:
<select>
<option id="id-1" value="some1">some1</option>
<option id="id-2" value="some2">some2</option>
<option id="id-3" value="some3">some3</option>
</select>
Instead the selected one.
For accessing the current option element from select you should rely on selectedIndex:
event.target[event.target.selectedIndex].id
The code:
export default class SelectForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "some1",
id: "id-1"
};
}
handleChange = event => {
console.log("event", event.target, event.target.selectedIndex);
this.setState({
value: event.target.value,
id: event.target[event.target.selectedIndex].id
});
};
render() {
return (
<div>
<select value={this.state.sex} onChange={this.handleChange}>
<option id="id-1" value="some1">some1</option>
<option id="id-2" value="some2">some2</option>
<option id="id-3" value="some3">some3</option>
</select>
ID: {this.state.id}
</div>
);
}
}
You can use selectedIndex attribute of select:
handleSubredditSelect(event) {
const selectedOption = event.target.childNodes[event.target.selectedIndex];
this.setState({
subreddit_selected: event.target.value,
subreddit_id: selectedOption.id,
}, () =>
this.props.history.push(`/${this.state.subreddit_selected}/new/`)
)
}
Here is the sandbox: https://codesandbox.io/s/n5679m5owj
AFAIK event.target.id should be working, doing the same in my project.
But should't it be
<Input
type="select"
onChange={(e) => this.handleSubredditSelect}
required>`
? (No parantheses after the methodname)

Categories