How to capture value selected from a dropdown list? - javascript

I have a simple form that I am creating using react JS. I have 2 text fields, Type & Year, and one dropdown which is car. The car dropdown is populated using a value coming from an API. The user enters text value for the first 2 fields and selects the value for car from the dropdown menu and hits add. I have another post API that captures this value. I can store and set the value for the text boxes but for the dropdown, the value of car is not getting captured and I get a null value. How can I store the value of car like I am doing for other 2 text boxes
import { useHistory } from "react-router-dom";
import axios from "axios";
import React,{useState,useEffect} from 'react';
function NewCar() {
const [type, setType] = useState("");
const [year, setYear] = useState("");
const [car, setCar] = useState("");
let history = useHistory();
const [entries,setEntries]=useState([])
useEffect(() => {
fetchValues();
}, [])
useEffect(() => {
console.log(entries)
}, [entries])
const fetchValues = async()=>{
const response = await axios('http://localhost:8080/cars');
setEntries(response.data)
}
const onSubmit = function (e) {
e.preventDefault();
axios
.post("http://localhost:8080/update", {
type,
year,
car
})
.then(() => history.push("/"));
};
return (
<form onSubmit={onSubmit}>
<table border="1">
Type:{" "}
<input
name="type"
value={type}
onChange={(e) => setType(e.target.value)}
/>
<br />
Year:{" "}
<textarea
name="year"
value={year}
onChange={(e) => setYear(e.target.value)}
></textarea>{" "}
<br />
<label for="car">Car:</label>
<select id="car" name="car">
{entries.map((entry) => (
<option value={entry.name}>{entry.name}</option>
))}
name="car"
value={car}
onChange={(e) => setCar(e.target.value)}
</select>
<br />
</table>
<button>Add</button>
</form>
);
}
export default NewCar;

I believe you made a mistake in your code by placing your attributes outside the select tag, it should look something like this:
<select
id="car"
name="car"
value={car}
onChange={(e) => setCar(e.target.value)}
>
{entries.map((entry) => (
<option value={entry.name}>{entry.name}</option>
))}
</select>
I have this sample in which u can get the the value of a select tag or dropdown, hope you can reference from it.
const [dataItem, setDataItem] = useState("");
const data = [
{
name: "Ford",
year: "1903",
},
{
name: "Chevy",
year: "1904",
},
{
name: "Dodge",
year: "1905",
},
{
name: "Honda",
year: "1906",
},
{
name: "Toyota",
year: "1907",
},
];
return (
<>
{dataItem}
<select
name="dataItem"
value={dataItem}
onChange={(e) => setDataItem(e.target.value)}
>
{data.map(({name}) => (
<option value={name}>{name}</option>
))}
</select>
</>
);
If it still went wrong, please let me know and write a comment below.

Related

My form is not updating when its. preloaded with default value - react

I have these input fields I want to already show default values in, but i also want to be able to update those values
here is the code- https://stackblitz.com/edit/react-vfcqkh?file=src/App.js
import React, { useState } from 'react';
import './style.css';
export default function App() {
const baseData = [
{nutrient: 'Energy',per100: '449kcal', per35: '157kcal', "id": 6 },
{nutrient: 'Fat',per100: '24.4g', per35: '8.6g', "id": 1 },
{ nutrient: 'Saturated fat',per100: '4.5g', per35: '1.6g', "id": 2 },
{ nutrient: 'Monounsaturated fat',per100: '13.6g', per35: '4.8g', "id": 3 }
];
// set original state as your data
const [data, setData] = useState(baseData);
const updateValue = e => {
const copiedData = [...data]; //to avoid mutating state
let index = copiedData.findIndex(obj => obj.id == e.target.id);
copiedData[index].nutrient = e.target.value;
setData(copiedData);
};
return (
<div>
<div>
<div>
{ Object.keys(baseData[0]).map(({id,nutrient}) => (
<input type="text" key={id} id={id} value={nutrient} name="productName" onChange={(e) => updateValue(e)} />
))}
</div>
{baseData.map((item) => (
<div key={item.id}>
{Object.values(item).map(({id,nutrient}) => (
<input type="text" key={id} id={id} value={nutrient} id="name" name="productName" onChange={(e) => updateValue(e)} />
))}
</div>
))}
</div>
</div>
);
}
I think you are trying to display the initial values of the form then while the user is typing or typed in values the value in the <input type="text" name="name"/> will be update or do you want to update the state with the value typed in the input field?
Displaying Default values
change the value prop/attribute of the input to defaultValue
<input type="text" key={id} id={id} defaultValue={nutrient} name="productName" onChange={(e) => updateValue(e)} />
This way user can type in value and the initial values will also be displayed.
Update values
in you update function, you can access the value that the user typed by doing this:
updateValue(e){
let value= e.target.value
// then do state update here
}
Re-defined Solution
This should work properly as I have tested the code and run it successfully
import React, {useState} from 'react'
import './style.css';
export default function App() {
const baseData = [
{ nutrient: "Energy", per100: "449kcal", per35: "157kcal", id: 6 },
{ nutrient: "Fat", per100: "24.4g", per35: "8.6g", id: 1 },
{ nutrient: "Saturated fat", per100: "4.5g", per35: "1.6g", id: 2 },
{ nutrient: "Monounsaturated fat", per100: "13.6g", per35: "4.8g", id: 3 },
];
// set original state as your data
const [data, setData] = useState(baseData);
const updateValue = (e) => {
const copiedData = [...data]; //to avoid mutating state
let index = copiedData.findIndex((obj) => obj.id === e.target.id);
copiedData[index].nutrient = e.target.value;
setData(copiedData);
};
// initialize vars to hold the baseData object keys and vals
let baseDataKeys = [],
baseDataVals = [];
// first get all the keys of the objects and store them in this var
baseData.forEach((keys) => {
baseDataKeys = Object.keys(keys);
});
// debug to see the keys
console.log(baseDataKeys);
// Secondly get all the values of the objects and store then in this var
baseData.forEach((vals) => {
baseDataVals = Object.values(vals);
});
// debug to see the valus
console.log(baseDataVals);
return (
<div>
<div>
<div>
{baseDataKeys.map((keys) => (
<input
type="text"
key={keys}
id={keys}
defaultValue={keys}
name="productName"
onChange={(e) => updateValue(e)}
/>
))}
</div>
{/* This is for the values */}
{baseDataVals.map((vals) => (
<div key={vals}>
<input
type="text"
id={vals}
defaultValue={vals}
id="name"
name="productName"
onChange={(e) => updateValue(e)}
/>
</div>
))}
</div>
</div>
);
}
Try this updated solution this is going to work accurately

Search does not work like expect in react

In my application i use a search input to search values, and select input also to filter values from my data. Now my component looks like below:
export default function App() {
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res= data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res= data.filter(i => i.age === Number(value));
setMydata(res);
}
return (
<div className="App">
<Select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Now, the functionality works. If you search something, appear results, and if you try to select a value, also appears the value that you selected.
Issue: If i select from dropdown, for example: 32, appears:
Julia is 32
Bill is 32
Bill is 32
And now if i want to search from the list above just Julia, i type Julia in search, it search from the whole list of data, not just from the list which i get after i selected 32. How to solve this, and how to get the result from the last results, not to search from the whole list, but from the last result?
Note: the same issue is when i search first and after that i select a value from dropdown.
Your two filters always work with the same object data, and not previously filtered state data myData. Best practice save value of filters in state and each render filter data:
export default function App() {
const [age, setAge] = useState('');
const [name, setName] = useState('');
const filteredData = data
.filter(i => Boolean(age) ? i.age === Number(age) : true)
.filter(i => i.name.toLowerCase().includes(name.toLowerCase()));
return (
<div className="App">
<Select value={age} style={{ width: 120 }} onChange={setAge}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input value={name} onChange={e => setName(e.target.value)} />
<h1>Data</h1>
{filteredData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Try this one out:
import React, { useState } from "react";
export default function App() {
const data = [
{ age: 2, name: 'John' },
{ age: 32,name: 'Mark' },
{ age: 22,name: 'Dell' },
{ age: 14,name: 'Linda' },
{ age: 16,name: 'Jon' },
{ age: 18,name: 'Ron' }
];
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res = data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res = data.filter(i => i.age === Number(value.target.value));
console.log("This is the value and respos", value.target.value);
setMydata(res);
}
return (
<div>
<select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<option value="2">2</option>
<option value="32">32</option>
</select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Here is the codesandbox demo: Demo

Select with value prop inside of Form.Item won't reflect any change of that value prop

I'm having trouble with antd forms in regard to Select input. I have two Select inputs and I need the second input to "reset" to the first option when the first input changes value.
I need to keep track of each Select value with React.useState because —in my real code— those values are used to make a request to a backend and re-populate other Select inputs but the first one, and each Select have a different amount of options, thus is desirable that they get "reset" to the first option.
How can I achieve what I'm looking for?
Since you decide to control the input field with value and onChange,
You don't need the Form name, remove it.
Then set the first hander, check if the value changed to decide whether set the second to default or not.
import React from "react";
import ReactDOM from "react-dom";
import { Select, Form } from "antd";
import "antd/dist/antd.css";
const fieldA = [{ value: 1, text: "Hello" }, { value: 2, text: "World" }];
const fieldB = [{ value: 1, text: "A" }, { value: 2, text: "B" }];
const App = () => {
const [valueA, setValueA] = React.useState(null);
const [valueB, setValueB] = React.useState(null);
const setFieldA = (value: number) => {
if (valueA !== value) {
setValueB(null);
}
setValueA(value);
};
const setFieldB = (value: number) => {
setValueB(value);
};
return (
<Form layout="vertical">
<Form.Item label="Field A">
<Select value={valueA} onChange={setFieldA}>
{fieldA.map(field => (
<Select.Option value={field.value}>{field.text}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="Field B">
<Select value={valueB} onChange={setFieldB}>
{fieldB.map(field => (
<Select.Option value={field.value}>{field.text}</Select.Option>
))}
</Select>
</Form.Item>
<div>
Field A "real" value: {valueA}
<br />
Field B "real" value: {valueB}
</div>
</Form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
If you add Select into the Form and Form.Item and set a name for Form.Item, the Select is completely controlled by the Form. You need to use the Form's api to control the Select or simply remove the name
import React from "react";
import ReactDOM from "react-dom";
import { Select, Form } from "antd";
import "antd/dist/antd.css";
const fieldA = [{ value: 1, text: "Hello" }, { value: 2, text: "World" }];
const fieldB = [{ value: 1, text: "A" }, { value: 2, text: "B" }];
const App = () => {
const [myvalues, setMyvalues] = React.useState({ valueA: 1, valueB: 1 });
const setFieldA = (value: number) => {
setMyvalues({ valueA: value, valueB: 1 });
form.setFieldsValue({ valueA: value, valueB: 1 });
};
const setFieldB = (value: number) => {
setMyvalues({ ...myvalues, valueB: value });
form.setFieldsValue({ ...myvalues, valueB: value });
};
const [form] = Form.useForm();
return (
<Form layout="vertical" form={form} initialValues={myvalues}>
<Form.Item name="valueA" label="Field A" shouldUpdate>
<Select value={myvalues.valueA} onChange={setFieldA}>
{fieldA.map(field => (
<Select.Option value={field.value}>{field.text}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item name="valueB" label="Field B" shouldUpdate>
<Select value={myvalues.valueB} onChange={setFieldB}>
{fieldB.map(field => (
<Select.Option value={field.value}>{field.text}</Select.Option>
))}
</Select>
</Form.Item>
<div>
Field A "real" value: {myvalues.valueA}.<br />
Field B "real" value: {myvalues.valueB}
</div>
</Form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can check here CodeSandBox. Hope it helps

React - how to use hooks to hide or show a component based on an onChange event

I'm trying to figure out how to use react hooks to hide or show a component based on an onChange event (a selected option being selected in a form).
I have a component called Animal, which will be hidden until someone selects 'animals' from a select menu in a form.
This is what I have tried to make:
import React, { useState, useEffect } from "react";
import useForm from "react-hook-form";
import { withRouter } from "react-router-dom";
import Select from "react-select";
import Animal from "./tips/Animal";
const specialFeatures = [
{ value: "animals", label: "Animals" },
{ value: "none", label: "None of these apply" }
];
const Method = props => {
const { register, handleSubmit, setValue, errors, reset } = useForm();
const [valuesSpecialFeatures, setSpecialFeatures] = useState([]);
const [showAnimal, setShowAnimal] = useState(false);
const handleMultiChangeSpecialFeatures = selectedOption => {
setValue("specialFeatures", selectedOption, true);
setSpecialFeatures(selectedOption);
if selectedOption === 'animals' setAnimal(!<Animal />)
};
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Does this proposal incldue any of these features?</label>
<Select
className="reactSelect"
name="specialFeatures"
placeholder="Select at least one"
value={valuesSpecialFeatures}
options={specialFeatures}
onChange={handleMultiChangeSpecialFeatures}
isMulti
/>
{ showAnimal && "animals" <Animal /> }
<input type="submit" value="next" />
</form>
</div>
);
};
export default withRouter(Method);
I'm trying to test if the form field named specialFeatures has a selectedOption that includes the value 'animals', then if it does, I want to display the Animal component beneath that field.
This attempt is clearly incorrect, but I can't see how to set the useEffect to toggle visibility.
First of all you need to set showAnimal state to a boolean and then use that to either show or hide the component:
const Method = props => {
const { register, handleSubmit, setValue, errors, reset } = useForm();
const [valuesSpecialFeatures, setSpecialFeatures] = useState([]);
const [showAnimal, setShowAnimal] = useState(false);
const handleMultiChangeSpecialFeatures = selectedOption => {
setValue("specialFeatures", selectedOption, true);
setSpecialFeatures(selectedOption);
setShowAnimal(selectedOption.some(option => option.value === "animals")); // Toggle 'showAnimal'
};
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Does this proposal incldue any of these features?</label>
<Select
className="reactSelect"
name="specialFeatures"
placeholder="Select at least one"
value={valuesSpecialFeatures}
options={specialFeatures}
onChange={handleMultiChangeSpecialFeatures}
isMulti
/>
{showAnimal && <Animal />} // Render component based on the toggle
<input type="submit" value="next" />
</form>
</div>
);
};
There's a simpler alternative though, which doesn't involve setting the value to the state, you can just derive it during render instead:
const Method = props => {
const { register, handleSubmit, setValue, errors, reset } = useForm();
const [valuesSpecialFeatures, setSpecialFeatures] = useState([]);
const handleMultiChangeSpecialFeatures = selectedOption => {
setValue("specialFeatures", selectedOption, true);
setSpecialFeatures(selectedOption);
};
// Derive the value based on the value of `valuesSpecialFeatures`
const isAnimalSelected = valuesSpecialFeatures.some(
option => option.value === "animals"
);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Does this proposal incldue any of these features?</label>
<Select
className="reactSelect"
name="specialFeatures"
placeholder="Select at least one"
value={valuesSpecialFeatures}
options={specialFeatures}
onChange={handleMultiChangeSpecialFeatures}
isMulti
/>
{isAnimalSelected && <Animal />} // Use derived value to render component
<input type="submit" value="next" />
</form>
</div>
);
};

React hooks: Can't update state inside function component

I'm new to React and am trying to figure out how to make a phonebook. I've gotten quite far but I'm having an issue I can't solve. I'm using React Hooks.
Everything works fine, except for when I call the setNewNumber('') and setNewName('') functions at the end of addPerson(), just before the filteredEntries const.
I want to reset the input fields in the form to empty strings ('') once the other code inside addPerson() is done running, but it seems like the two functions are never called since the value for newName and newNumber don't change to '' (instead they keep the values the user added). However, my other useState functions (setPersons() and setFilteredPersons()) work inside addPerson()...
I've tried reading the documentation and asking around but haven't found a solution. I'd be very grateful for any clues/help!
import React, { useState } from 'react'
import Person from './components/Person'
const App = () => {
const [ persons, setPersons ] = useState([
{ name: 'Cat', number: '111' },
{ name: 'Dog', number: '222' },
{ name: 'Horse', number: '333' },
{ name: 'Owl', number: '444' }
])
const [filteredPersons, setFilteredPersons] = useState([...persons])
const [ newName, setNewName ] = useState('')
const [ newNumber, setNewNumber ] = useState('')
const addPerson = (event) => {
event.preventDefault()
const createPerson = () => {
const personObject = {
name: newName,
number: newNumber,
}
setPersons([...persons, personObject])
setFilteredPersons([...persons, personObject]) //varför går det inte att bara köra [...persons?]
}
const upperCaseNewName = newName.toUpperCase()
let doubleName
persons.map(person => {
const upperCasePerson = person.name.toUpperCase()
if(upperCaseNewName === upperCasePerson) {
doubleName = upperCasePerson
}
return doubleName
})
if (doubleName === undefined) {
createPerson()
} else if(doubleName === upperCaseNewName) {
alert(`${newName} is already in the phonebook`)
}
setNewName('')
setNewNumber('')
}
const filterEntries = event => {
let filtered = persons.filter(person => {
return person.name.toUpperCase().indexOf(event.target.value.toUpperCase()) !== -1
})
setFilteredPersons(filtered)
}
const renderPersons = () => filteredPersons.map(person =>
<Person key={person.name} name={person.name} number={person.number}/>
)
return (
<div>
<h2>Phonebook</h2>
<p>Filter entries:</p> <input onChange={(event) => filterEntries(event)}/>
<form>
<div>
name: <input onChange={(event) => setNewName(event.target.value)}/>
<br/>
phone: <input onChange={(event) => setNewNumber(event.target.value)}/>
</div>
<div>
<button type="submit" onClick={addPerson}>add</button>
</div>
</form>
<h2>Numbers</h2>
{renderPersons()}
</div>
)
}
export default App
The person component at the top just contains this code:
import React from 'react'
const Person = (props) => {
return(
<>
<p>{props.name} {props.number}</p>
</>
)
}
export default Person
Your components are not actually tied to your state values. You need them to be "controlled." Check out the docs for more examples :)
https://reactjs.org/docs/forms.html#controlled-components
<input value={newName} onChange={event => setNewName(event.target.value)} />
The reset is working correctly. What you forgot to do is add the value to each input. Without the value attribute the input is considered an uncontrolled component. By the sounds of it, you're looking to control the value via code.
Change
<div>
name: <input onChange={event => setNewName(event.target.value)} />
<br />
phone: <input onChange={event => setNewNumber(event.target.value)} />
</div>
to
<div>
name: <input value={newName} onChange={event => setNewName(event.target.value)} />
<br />
phone: <input value={newNumber} onChange={event => setNewNumber(event.target.value)} />
</div>
Codesandbox Demo
You have missed adding value attribute to input and thus your component is not the "Controlled" component.
You can read more here.
Changes needed
<input
value={newName}
onChange={event => setNewName(event.target.value)}
/>
<br />
phone:
<input
value={newNumber}
onChange={event => setNewNumber(event.target.value)}
/>
Codesandbox Link: https://codesandbox.io/s/crimson-frog-ecp98
Hope this Helps!

Categories

Resources